memento-mcp-server 1.16.3-a → 1.16.3-b

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 (68) hide show
  1. package/dist/domains/search/algorithms/vector-search-engine-migration.d.ts +13 -8
  2. package/dist/domains/search/algorithms/vector-search-engine-migration.d.ts.map +1 -1
  3. package/dist/domains/search/algorithms/vector-search-engine-migration.js +19 -41
  4. package/dist/domains/search/algorithms/vector-search-engine-migration.js.map +1 -1
  5. package/dist/domains/search/algorithms/vector-search-engine.d.ts +17 -36
  6. package/dist/domains/search/algorithms/vector-search-engine.d.ts.map +1 -1
  7. package/dist/domains/search/algorithms/vector-search-engine.js +94 -481
  8. package/dist/domains/search/algorithms/vector-search-engine.js.map +1 -1
  9. package/dist/domains/search/repositories/vector-search.repository.d.ts.map +1 -1
  10. package/dist/domains/search/repositories/vector-search.repository.js +28 -12
  11. package/dist/domains/search/repositories/vector-search.repository.js.map +1 -1
  12. package/dist/server/http-server.d.ts.map +1 -1
  13. package/dist/server/http-server.js +2 -7
  14. package/dist/server/http-server.js.map +1 -1
  15. package/dist/server/index.d.ts +3 -0
  16. package/dist/server/index.d.ts.map +1 -1
  17. package/dist/server/index.js +33 -7
  18. package/dist/server/index.js.map +1 -1
  19. package/dist/server/server-factory.d.ts +65 -0
  20. package/dist/server/server-factory.d.ts.map +1 -0
  21. package/dist/server/server-factory.js +40 -0
  22. package/dist/server/server-factory.js.map +1 -0
  23. package/dist/server/servers/sse-server.d.ts +33 -0
  24. package/dist/server/servers/sse-server.d.ts.map +1 -0
  25. package/dist/server/servers/sse-server.js +48 -0
  26. package/dist/server/servers/sse-server.js.map +1 -0
  27. package/dist/server/servers/stdio-server.d.ts +34 -0
  28. package/dist/server/servers/stdio-server.d.ts.map +1 -0
  29. package/dist/server/servers/stdio-server.js +58 -0
  30. package/dist/server/servers/stdio-server.js.map +1 -0
  31. package/dist/server/simple-mcp-server.d.ts +5 -0
  32. package/dist/server/simple-mcp-server.d.ts.map +1 -1
  33. package/dist/server/simple-mcp-server.js +17 -7
  34. package/dist/server/simple-mcp-server.js.map +1 -1
  35. package/dist/server/sse-server-impl.d.ts +22 -0
  36. package/dist/server/sse-server-impl.d.ts.map +1 -0
  37. package/dist/server/sse-server-impl.js +39 -0
  38. package/dist/server/sse-server-impl.js.map +1 -0
  39. package/dist/server/stdio-server-impl.d.ts +12 -0
  40. package/dist/server/stdio-server-impl.d.ts.map +1 -0
  41. package/dist/server/stdio-server-impl.js +19 -0
  42. package/dist/server/stdio-server-impl.js.map +1 -0
  43. package/dist/shared/types/vector-search.types.d.ts +1 -0
  44. package/dist/shared/types/vector-search.types.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. package/scripts/__tests__/check-db-integrity.integration.spec.ts +163 -0
  47. package/scripts/__tests__/fix-migration.integration.spec.ts +203 -0
  48. package/scripts/__tests__/migrate-embedding-data.integration.spec.ts +219 -0
  49. package/scripts/__tests__/regenerate-embeddings.integration.spec.ts +192 -0
  50. package/scripts/backup-embeddings.js +52 -61
  51. package/scripts/check-db-integrity.js +49 -25
  52. package/scripts/check-file-sizes.ts +4 -4
  53. package/scripts/check-pii-masking.ts +0 -3
  54. package/scripts/check-sql-injection.ts +0 -12
  55. package/scripts/debug-embeddings.js +74 -93
  56. package/scripts/fix-migration.js +115 -80
  57. package/scripts/fix-vector-dimensions.js +70 -89
  58. package/scripts/migrate-embedding-data.js +111 -25
  59. package/scripts/regenerate-embeddings.js +31 -15
  60. package/scripts/run-migration.js +144 -107
  61. package/scripts/safe-migration.js +192 -142
  62. package/scripts/save-work-memory.ts +6 -7
  63. package/scripts/simple-migrate.js +66 -34
  64. package/scripts/simple-update.js +147 -109
  65. package/dist/domains/search/algorithms/vector-search-engine-refactored.d.ts +0 -56
  66. package/dist/domains/search/algorithms/vector-search-engine-refactored.d.ts.map +0 -1
  67. package/dist/domains/search/algorithms/vector-search-engine-refactored.js +0 -101
  68. package/dist/domains/search/algorithms/vector-search-engine-refactored.js.map +0 -1
@@ -3,9 +3,17 @@
3
3
  /**
4
4
  * 임베딩 백업 스크립트
5
5
  * 기존 벡터값을 백업한 후 삭제하고 재생성하는 스크립트
6
+ *
7
+ * 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
8
+ *
9
+ * 사용법:
10
+ * - 개발 환경: npx tsx scripts/backup-embeddings.js
11
+ * - 프로덕션: npm run build && node dist/scripts/backup-embeddings.js
6
12
  */
7
13
 
8
- import Database from 'better-sqlite3';
14
+ // TypeScript 소스를 직접 import (tsx로 실행 시)
15
+ // 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
16
+ import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
9
17
  import fs from 'fs';
10
18
  import path from 'path';
11
19
  import { fileURLToPath } from 'url';
@@ -14,17 +22,9 @@ import { validateFilePath, sanitizeFileName } from '../src/shared/utils/path-val
14
22
  const __filename = fileURLToPath(import.meta.url);
15
23
  const __dirname = path.dirname(__filename);
16
24
 
17
- // 데이터베이스 경로 설정
25
+ // 백업 디렉토리 설정
18
26
  // PRD 0019: 보안 강화 (Phase 1) - Path Traversal 방지
19
- const dbPath = process.env.DB_PATH || path.join(__dirname, '..', 'data', 'memory.db');
20
- if (!validateFilePath(dbPath, 'data')) {
21
- throw new Error(
22
- `Path Traversal 방지: 허용되지 않은 데이터베이스 경로입니다. ` +
23
- `경로: ${dbPath}`
24
- );
25
- }
26
-
27
- const backupDir = path.join(__dirname, '..', 'backup');
27
+ const backupDir = path.join(process.cwd(), 'backup');
28
28
  if (!validateFilePath(backupDir, 'backup')) {
29
29
  throw new Error(
30
30
  `Path Traversal 방지: 허용되지 않은 백업 디렉토리 경로입니다. ` +
@@ -46,83 +46,74 @@ async function backupEmbeddings() {
46
46
  fs.mkdirSync(backupDir, { recursive: true });
47
47
  }
48
48
 
49
- // 데이터베이스 연결
50
- const db = new Database(dbPath);
49
+ let db = null;
51
50
 
52
51
  try {
53
- // 기존 임베딩 데이터 조회
52
+ // 공통 모듈을 사용하여 데이터베이스 초기화
53
+ // initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
54
+ db = await initializeDatabase();
55
+
56
+ // 모든 임베딩 데이터 조회
54
57
  const embeddings = db.prepare(`
55
58
  SELECT
56
- me.memory_id,
57
- me.embedding,
58
- me.dim,
59
- me.model,
60
- me.created_at,
61
- mi.content,
62
- mi.type
63
- FROM memory_embedding me
64
- JOIN memory_item mi ON me.memory_id = mi.id
65
- ORDER BY me.created_at
59
+ memory_id,
60
+ embedding,
61
+ dim,
62
+ model,
63
+ created_at
64
+ FROM memory_embedding
66
65
  `).all();
67
66
 
68
- console.log(`📊 발견된 임베딩 개수: ${embeddings.length}`);
67
+ console.log(`📊 백업할 임베딩 개수: ${embeddings.length}`);
69
68
 
70
69
  if (embeddings.length === 0) {
71
70
  console.log('⚠️ 백업할 임베딩이 없습니다.');
72
71
  return;
73
72
  }
74
73
 
75
- // 차원별 통계
76
- const dimensionStats = {};
77
- embeddings.forEach(emb => {
78
- const dim = emb.dim;
79
- dimensionStats[dim] = (dimensionStats[dim] || 0) + 1;
80
- });
81
-
82
- console.log('📈 차원별 통계:');
83
- Object.entries(dimensionStats).forEach(([dim, count]) => {
84
- console.log(` - ${dim}차원: ${count}개`);
85
- });
86
-
87
- // 백업 데이터 생성
74
+ // 백업 데이터 준비
88
75
  const backupData = {
89
76
  timestamp: new Date().toISOString(),
90
- totalEmbeddings: embeddings.length,
91
- dimensionStats,
92
- embeddings: embeddings.map(emb => ({
93
- memory_id: emb.memory_id,
94
- content: emb.content,
95
- type: emb.type,
96
- embedding: JSON.parse(emb.embedding),
97
- dim: emb.dim,
98
- model: emb.model,
99
- created_at: emb.created_at
77
+ count: embeddings.length,
78
+ embeddings: embeddings.map(e => ({
79
+ memory_id: e.memory_id,
80
+ embedding: e.embedding,
81
+ dim: e.dim,
82
+ model: e.model,
83
+ created_at: e.created_at
100
84
  }))
101
85
  };
102
86
 
103
- // 백업 파일 저장
104
- fs.writeFileSync(backupFile, JSON.stringify(backupData, null, 2));
87
+ // JSON 파일로 저장
88
+ fs.writeFileSync(backupFile, JSON.stringify(backupData, null, 2), 'utf8');
105
89
  console.log(`✅ 백업 완료: ${backupFile}`);
90
+ console.log(`📦 백업 크기: ${(fs.statSync(backupFile).size / 1024).toFixed(2)} KB`);
106
91
 
107
- // 임베딩 테이블 삭제
108
- console.log('🗑️ 기존 임베딩 삭제 중...');
109
- const deleteResult = db.prepare('DELETE FROM memory_embedding').run();
110
- console.log(`✅ 삭제 완료: ${deleteResult.changes}개 행 삭제`);
111
-
112
- console.log('🎉 백업 및 삭제 완료!');
113
- console.log('다음 단계: npm run regenerate-embeddings');
92
+ // 사용자 확인
93
+ console.log('\n⚠️ 백업이 완료되었습니다.');
94
+ console.log('다음 단계로 임베딩을 삭제하고 재생성할 수 있습니다.');
95
+ console.log('백업 파일:', backupFile);
114
96
 
115
97
  } catch (error) {
116
- console.error('❌ 백업 실패:', error);
98
+ console.error('❌ 백업 실패:', error.message);
99
+ if (error.stack) {
100
+ console.error(' 스택 트레이스:', error.stack);
101
+ }
117
102
  process.exit(1);
118
103
  } finally {
119
- db.close();
104
+ // 데이터베이스 연결 종료
105
+ if (db) {
106
+ closeDatabase(db);
107
+ }
120
108
  }
121
109
  }
122
110
 
123
111
  // 스크립트 실행
124
- if (import.meta.url === `file://${process.argv[1]}`) {
125
- backupEmbeddings().catch(console.error);
112
+ if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
113
+ backupEmbeddings().catch((error) => {
114
+ console.error('❌ 스크립트 실행 중 오류 발생:', error);
115
+ process.exit(1);
116
+ });
126
117
  }
127
118
 
128
119
  export { backupEmbeddings };
@@ -1,46 +1,56 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Memento 데이터베이스 무결성 검사 스크립트
4
- * 사용법: node scripts/check-db-integrity.js
4
+ *
5
+ * 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
6
+ *
7
+ * 사용법:
8
+ * - 개발 환경: npx tsx scripts/check-db-integrity.js
9
+ * - 프로덕션: npm run build && node dist/scripts/check-db-integrity.js
5
10
  */
6
11
 
7
- const Database = require('better-sqlite3');
8
- const fs = require('fs');
9
- const path = require('path');
12
+ // TypeScript 소스를 직접 import (tsx로 실행 시)
13
+ // 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
14
+ import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
15
+ import { existsSync, mkdirSync, appendFileSync } from 'fs';
16
+ import { join } from 'path';
10
17
 
11
- const DB_PATH = './data/memory.db';
12
18
  const LOG_PATH = './logs/db-integrity.log';
13
19
 
14
20
  // 로그 디렉토리 생성
15
- if (!fs.existsSync('./logs')) {
16
- fs.mkdirSync('./logs');
21
+ if (!existsSync('./logs')) {
22
+ mkdirSync('./logs', { recursive: true });
17
23
  }
18
24
 
25
+ /**
26
+ * 로그 메시지 출력 및 파일 기록
27
+ */
19
28
  function log(message) {
20
29
  const timestamp = new Date().toISOString();
21
30
  const logMessage = `[${timestamp}] ${message}\n`;
22
31
  console.log(message);
23
- fs.appendFileSync(LOG_PATH, logMessage);
32
+ appendFileSync(LOG_PATH, logMessage);
24
33
  }
25
34
 
26
- function checkDatabaseIntegrity() {
35
+ /**
36
+ * 데이터베이스 무결성 검사
37
+ *
38
+ * @returns {Promise<boolean>} 검사 통과 여부
39
+ */
40
+ async function checkDatabaseIntegrity() {
27
41
  log('데이터베이스 무결성 검사 시작...');
28
42
 
43
+ let db = null;
44
+
29
45
  try {
30
- // 데이터베이스 파일 존재 확인
31
- if (!fs.existsSync(DB_PATH)) {
32
- log('❌ 데이터베이스 파일이 존재하지 않습니다.');
33
- return false;
34
- }
35
-
36
- // 데이터베이스 연결 테스트
37
- const db = new Database(DB_PATH);
46
+ // 공통 모듈을 사용하여 데이터베이스 초기화
47
+ // initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
48
+ db = await initializeDatabase();
38
49
 
39
50
  // PRAGMA integrity_check 실행
40
51
  const integrityResult = db.prepare('PRAGMA integrity_check').get();
41
52
  if (integrityResult.integrity_check !== 'ok') {
42
53
  log(`❌ 데이터베이스 무결성 검사 실패: ${integrityResult.integrity_check}`);
43
- db.close();
44
54
  return false;
45
55
  }
46
56
 
@@ -52,7 +62,7 @@ function checkDatabaseIntegrity() {
52
62
 
53
63
  if (tables.length < 3) {
54
64
  log('❌ 필수 테이블이 누락되었습니다.');
55
- db.close();
65
+ log(` 발견된 테이블: ${tables.map(t => t.name).join(', ')}`);
56
66
  return false;
57
67
  }
58
68
 
@@ -65,17 +75,27 @@ function checkDatabaseIntegrity() {
65
75
  log(` - 임베딩: ${embeddingCount.count}개`);
66
76
  log(` - 테이블: ${tables.length}개`);
67
77
 
68
- db.close();
69
78
  return true;
70
79
 
71
80
  } catch (error) {
72
81
  log(`❌ 데이터베이스 검사 중 오류 발생: ${error.message}`);
82
+ if (error.stack) {
83
+ log(` 스택 트레이스: ${error.stack}`);
84
+ }
73
85
  return false;
86
+ } finally {
87
+ // 데이터베이스 연결 종료
88
+ if (db) {
89
+ closeDatabase(db);
90
+ }
74
91
  }
75
92
  }
76
93
 
77
- function main() {
78
- const isHealthy = checkDatabaseIntegrity();
94
+ /**
95
+ * 메인 함수
96
+ */
97
+ async function main() {
98
+ const isHealthy = await checkDatabaseIntegrity();
79
99
 
80
100
  if (!isHealthy) {
81
101
  log('🚨 데이터베이스에 문제가 있습니다. 백업에서 복구를 고려하세요.');
@@ -86,8 +106,12 @@ function main() {
86
106
  }
87
107
  }
88
108
 
89
- if (require.main === module) {
90
- main();
109
+ // 스크립트가 직접 실행될 때만 main 함수 호출
110
+ if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
111
+ main().catch((error) => {
112
+ console.error('❌ 스크립트 실행 중 오류 발생:', error);
113
+ process.exit(1);
114
+ });
91
115
  }
92
116
 
93
- module.exports = { checkDatabaseIntegrity };
117
+ export { checkDatabaseIntegrity };
@@ -9,7 +9,7 @@
9
9
  * tsx scripts/check-file-sizes.ts --ci
10
10
  * tsx scripts/check-file-sizes.ts --threshold 500
11
11
  * tsx scripts/check-file-sizes.ts --directory src/
12
- * tsx scripts/check-file-sizes.ts --exclude "**/*.spec.ts"
12
+ * tsx scripts/check-file-sizes.ts --exclude '*.spec.ts'
13
13
  *
14
14
  * 목표:
15
15
  * - 핵심 핸들러/서비스 파일이 500줄 이하
@@ -17,7 +17,7 @@
17
17
  * - 경고/에러 출력
18
18
  */
19
19
 
20
- import { readFileSync, statSync } from 'fs';
20
+ import { readFileSync } from 'fs';
21
21
  import { readdir } from 'fs/promises';
22
22
  import { join, relative } from 'path';
23
23
 
@@ -111,7 +111,7 @@ function printHelp(): void {
111
111
  tsx scripts/check-file-sizes.ts
112
112
  tsx scripts/check-file-sizes.ts --ci
113
113
  tsx scripts/check-file-sizes.ts --threshold 500
114
- tsx scripts/check-file-sizes.ts --directory src/ --exclude "**/*.spec.ts"
114
+ tsx scripts/check-file-sizes.ts --directory src/ --exclude '*.spec.ts'
115
115
  `);
116
116
  }
117
117
 
@@ -164,7 +164,7 @@ function validateFileSize(filePath: string, threshold: number): FileSizeResult {
164
164
  * 패턴 매칭 (간단한 glob 패턴 지원)
165
165
  *
166
166
  * @param path - 파일 경로
167
- * @param pattern - 패턴 (예: "**/node_modules/**", "**/*.spec.ts")
167
+ * @param pattern - 패턴 (예: node_modules, *.spec.ts)
168
168
  * @returns 매칭 여부
169
169
  */
170
170
  function matchesPattern(path: string, pattern: string): boolean {
@@ -176,9 +176,6 @@ function checkFile(filePath: string): PIIMaskingLocation[] {
176
176
  // 로거 파일인지 확인 (logger, file-logger, error-logging-service 등)
177
177
  const isLoggerFile = /logger|log|error-logging/i.test(relativePath);
178
178
 
179
- // PIIMasker import 확인
180
- const hasPIIMaskerImport = /import.*PIIMasker|from.*pii-masker/.test(content);
181
-
182
179
  // logger.error, logger.warn, logger.info, logger.debug 호출 확인
183
180
  // logger.ts를 import하는 경우는 이미 마스킹이 적용되어 있으므로 제외
184
181
  if (!usesLoggerUtils) {
@@ -183,18 +183,6 @@ function findSqlInjectionPatterns(filePath: string): SqlInjectionLocation[] {
183
183
  const content = readFileSync(filePath, 'utf-8');
184
184
  const lines = content.split('\n');
185
185
 
186
- // 패턴 1: 문자열 연결을 통한 SQL 쿼리 생성
187
- // sql +=, query +=, sql = sql + 등
188
- const stringConcatenationPattern = /\b(sql|query|stmt|statement)\s*([+]=|=.*\+)/gi;
189
-
190
- // 패턴 2: 템플릿 리터럴로 동적 테이블명/컬럼명 사용
191
- // FROM ${, JOIN ${, WHERE ${ 등 (일부는 허용 가능하지만 검사 대상)
192
- const templateLiteralPattern = /\b(FROM|JOIN|WHERE|SELECT|INSERT|UPDATE|DELETE|INTO|SET)\s+\$\{/gi;
193
-
194
- // 패턴 3: 파라미터 바인딩 미사용 (문자열 연결)
195
- // '...' + variable, "..." + variable (SQL 쿼리 컨텍스트에서)
196
- const parameterBindingPattern = /(['"]).*?\1\s*\+\s*\w+/g;
197
-
198
186
  for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
199
187
  const line = lines[lineIndex];
200
188
  const trimmedLine = line.trim();
@@ -3,25 +3,28 @@
3
3
  /**
4
4
  * 임베딩 디버깅 스크립트
5
5
  * 현재 데이터베이스의 임베딩 상태를 상세히 분석
6
+ *
7
+ * 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
8
+ *
9
+ * 사용법:
10
+ * - 개발 환경: npx tsx scripts/debug-embeddings.js
11
+ * - 프로덕션: npm run build && node dist/scripts/debug-embeddings.js
6
12
  */
7
13
 
8
- import Database from 'better-sqlite3';
9
- import path from 'path';
10
- import { fileURLToPath } from 'url';
11
-
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = path.dirname(__filename);
14
-
15
- // 데이터베이스 경로 설정
16
- const dbPath = process.env.DB_PATH || path.join(__dirname, '..', 'data', 'memory.db');
14
+ // TypeScript 소스를 직접 import (tsx로 실행 시)
15
+ // 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
16
+ import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
17
17
 
18
18
  async function debugEmbeddings() {
19
19
  console.log('🔍 임베딩 상태 디버깅 시작...');
20
20
 
21
- // 데이터베이스 연결
22
- const db = new Database(dbPath);
21
+ let db = null;
23
22
 
24
23
  try {
24
+ // 공통 모듈을 사용하여 데이터베이스 초기화
25
+ // initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
26
+ db = await initializeDatabase();
27
+
25
28
  // 1. 전체 임베딩 통계
26
29
  console.log('\n📊 전체 임베딩 통계:');
27
30
  const totalStats = db.prepare(`
@@ -49,21 +52,18 @@ async function debugEmbeddings() {
49
52
  GROUP_CONCAT(memory_id) as memory_ids
50
53
  FROM memory_embedding
51
54
  GROUP BY dim
52
- ORDER BY dim
55
+ ORDER BY count DESC
53
56
  `).all();
54
57
 
55
58
  dimensionStats.forEach(stat => {
56
59
  console.log(`- ${stat.dim}차원: ${stat.count}개`);
57
- if (stat.count <= 5) {
58
- console.log(` 메모리 ID: ${stat.memory_ids}`);
59
- }
60
60
  });
61
61
 
62
62
  // 3. 모델별 분포
63
63
  console.log('\n🤖 모델별 분포:');
64
64
  const modelStats = db.prepare(`
65
65
  SELECT
66
- model,
66
+ COALESCE(model, 'NULL') as model,
67
67
  COUNT(*) as count,
68
68
  AVG(dim) as avg_dim
69
69
  FROM memory_embedding
@@ -72,113 +72,94 @@ async function debugEmbeddings() {
72
72
  `).all();
73
73
 
74
74
  modelStats.forEach(stat => {
75
- console.log(`- ${stat.model || 'NULL'}: ${stat.count}개 (평균 ${stat.avg_dim?.toFixed(1)}차원)`);
75
+ console.log(`- ${stat.model}: ${stat.count}개 (평균 ${stat.avg_dim?.toFixed(1) || 'N/A'}차원)`);
76
76
  });
77
77
 
78
- // 4. 최근 생성된 임베딩들
79
- console.log('\n🕒 최근 생성된 임베딩 (최대 10개):');
80
- const recentEmbeddings = db.prepare(`
78
+ // 4. 임베딩 제공자별 분포 (컬럼이 있는 경우)
79
+ const hasProvider = db.prepare("PRAGMA table_info(memory_embedding)").all()
80
+ .some(col => col.name === 'embedding_provider');
81
+
82
+ if (hasProvider) {
83
+ console.log('\n🔧 임베딩 제공자별 분포:');
84
+ const providerStats = db.prepare(`
85
+ SELECT
86
+ COALESCE(embedding_provider, 'NULL') as provider,
87
+ COUNT(*) as count,
88
+ AVG(dimensions) as avg_dim
89
+ FROM memory_embedding
90
+ GROUP BY embedding_provider
91
+ ORDER BY count DESC
92
+ `).all();
93
+
94
+ providerStats.forEach(stat => {
95
+ console.log(`- ${stat.provider}: ${stat.count}개 (평균 ${stat.avg_dim?.toFixed(1) || 'N/A'}차원)`);
96
+ });
97
+ }
98
+
99
+ // 5. 문제가 있는 임베딩 확인
100
+ console.log('\n⚠️ 문제가 있는 임베딩:');
101
+ const problematic = db.prepare(`
81
102
  SELECT
82
103
  memory_id,
83
104
  dim,
84
105
  model,
85
- created_at,
86
- LENGTH(embedding) as embedding_length
106
+ CASE
107
+ WHEN embedding IS NULL OR embedding = '' THEN '빈 임베딩'
108
+ WHEN dim IS NULL OR dim = 0 THEN '차원 없음'
109
+ ELSE '정상'
110
+ END as issue
87
111
  FROM memory_embedding
88
- ORDER BY created_at DESC
112
+ WHERE embedding IS NULL OR embedding = '' OR dim IS NULL OR dim = 0
89
113
  LIMIT 10
90
114
  `).all();
91
115
 
92
- recentEmbeddings.forEach(emb => {
93
- console.log(`- ${emb.memory_id}: ${emb.dim}차원, ${emb.model}, ${emb.created_at}`);
94
- });
95
-
96
- // 5. 문제가 있는 임베딩 찾기
97
- console.log('\n⚠️ 문제가 있을 수 있는 임베딩들:');
98
-
99
- // 차원이 0인 경우
100
- const zeroDim = db.prepare(`
101
- SELECT memory_id, dim, model FROM memory_embedding WHERE dim = 0
102
- `).all();
103
-
104
- if (zeroDim.length > 0) {
105
- console.log(`- 차원이 0인 임베딩: ${zeroDim.length}개`);
106
- zeroDim.forEach(emb => {
107
- console.log(` ${emb.memory_id} (${emb.model})`);
116
+ if (problematic.length === 0) {
117
+ console.log('- 문제가 있는 임베딩이 없습니다.');
118
+ } else {
119
+ problematic.forEach(item => {
120
+ console.log(`- ${item.memory_id}: ${item.issue} (차원: ${item.dim || 'N/A'}, 모델: ${item.model || 'N/A'})`);
108
121
  });
109
122
  }
110
123
 
111
- // 차원이 매우 경우 (1536보다 큰 경우)
112
- const largeDim = db.prepare(`
113
- SELECT memory_id, dim, model FROM memory_embedding WHERE dim > 1536
114
- `).all();
115
-
116
- if (largeDim.length > 0) {
117
- console.log(`- 차원이 1536보다 큰 임베딩: ${largeDim.length}개`);
118
- largeDim.forEach(emb => {
119
- console.log(` ${emb.memory_id}: ${emb.dim}차원 (${emb.model})`);
120
- });
121
- }
122
-
123
- // 6. 임베딩 데이터 샘플 확인
124
- console.log('\n🔬 임베딩 데이터 샘플 (첫 3개):');
125
- const samples = db.prepare(`
124
+ // 6. 최근 생성된 임베딩
125
+ console.log('\n🕐 최근 생성된 임베딩 (최대 5개):');
126
+ const recent = db.prepare(`
126
127
  SELECT
127
128
  memory_id,
128
129
  dim,
129
130
  model,
130
- SUBSTR(embedding, 1, 100) as embedding_preview
131
+ created_at
131
132
  FROM memory_embedding
132
- LIMIT 3
133
+ ORDER BY created_at DESC
134
+ LIMIT 5
133
135
  `).all();
134
136
 
135
- samples.forEach((sample, index) => {
136
- console.log(`\n샘플 ${index + 1}:`);
137
- console.log(`- 메모리 ID: ${sample.memory_id}`);
138
- console.log(`- 차원: ${sample.dim}`);
139
- console.log(`- 모델: ${sample.model}`);
140
- console.log(`- 임베딩 미리보기: ${sample.embedding_preview}...`);
141
-
142
- // 실제 벡터 길이 확인
143
- try {
144
- const fullEmbedding = db.prepare(`
145
- SELECT embedding FROM memory_embedding WHERE memory_id = ?
146
- `).get(sample.memory_id);
147
-
148
- const vector = JSON.parse(fullEmbedding.embedding);
149
- console.log(`- 실제 벡터 길이: ${vector.length}`);
150
- console.log(`- 첫 5개 값: [${vector.slice(0, 5).join(', ')}...]`);
151
- } catch (error) {
152
- console.log(`- 벡터 파싱 오류: ${error.message}`);
153
- }
137
+ recent.forEach(item => {
138
+ console.log(`- ${item.memory_id}: ${item.dim}차원, ${item.model || 'N/A'} (${item.created_at})`);
154
139
  });
155
140
 
156
- // 7. 메모리 아이템과의 연결 상태 확인
157
- console.log('\n🔗 메모리 아이템 연결 상태:');
158
- const connectionStats = db.prepare(`
159
- SELECT
160
- (SELECT COUNT(*) FROM memory_item) as total_memories,
161
- (SELECT COUNT(*) FROM memory_embedding) as total_embeddings,
162
- (SELECT COUNT(*) FROM memory_item mi
163
- LEFT JOIN memory_embedding me ON mi.id = me.memory_id
164
- WHERE me.memory_id IS NULL) as memories_without_embedding
165
- `).get();
166
-
167
- console.log(`- 총 메모리 개수: ${connectionStats.total_memories}`);
168
- console.log(`- 총 임베딩 개수: ${connectionStats.total_embeddings}`);
169
- console.log(`- 임베딩이 없는 메모리: ${connectionStats.memories_without_embedding}`);
141
+ console.log('\n✅ 디버깅 완료!');
170
142
 
171
143
  } catch (error) {
172
- console.error('❌ 디버깅 실패:', error);
144
+ console.error('❌ 디버깅 중 오류 발생:', error.message);
145
+ if (error.stack) {
146
+ console.error(' 스택 트레이스:', error.stack);
147
+ }
173
148
  process.exit(1);
174
149
  } finally {
175
- db.close();
150
+ // 데이터베이스 연결 종료
151
+ if (db) {
152
+ closeDatabase(db);
153
+ }
176
154
  }
177
155
  }
178
156
 
179
157
  // 스크립트 실행
180
- if (import.meta.url === `file://${process.argv[1]}`) {
181
- debugEmbeddings().catch(console.error);
158
+ if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
159
+ debugEmbeddings().catch((error) => {
160
+ console.error('❌ 스크립트 실행 중 오류 발생:', error);
161
+ process.exit(1);
162
+ });
182
163
  }
183
164
 
184
165
  export { debugEmbeddings };