memento-mcp-server 1.16.3-a → 1.16.3-c
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/dist/domains/search/algorithms/vector-search-engine-migration.d.ts +13 -8
- package/dist/domains/search/algorithms/vector-search-engine-migration.d.ts.map +1 -1
- package/dist/domains/search/algorithms/vector-search-engine-migration.js +19 -41
- package/dist/domains/search/algorithms/vector-search-engine-migration.js.map +1 -1
- package/dist/domains/search/algorithms/vector-search-engine.d.ts +17 -36
- package/dist/domains/search/algorithms/vector-search-engine.d.ts.map +1 -1
- package/dist/domains/search/algorithms/vector-search-engine.js +94 -481
- package/dist/domains/search/algorithms/vector-search-engine.js.map +1 -1
- package/dist/domains/search/repositories/vector-search.repository.d.ts.map +1 -1
- package/dist/domains/search/repositories/vector-search.repository.js +28 -12
- package/dist/domains/search/repositories/vector-search.repository.js.map +1 -1
- package/dist/domains/search/services/vector-search/vector-performance-tester.js +1 -1
- package/dist/domains/search/services/vector-search/vector-performance-tester.js.map +1 -1
- package/dist/domains/search/services/vector-search/vector-search-container.d.ts +1 -1
- package/dist/domains/search/services/vector-search/vector-search-container.d.ts.map +1 -1
- package/dist/domains/search/services/vector-search/vector-search-container.js +1 -1
- package/dist/domains/search/services/vector-search/vector-search-container.js.map +1 -1
- package/dist/domains/search/services/vector-search/vector-search-facade.js +3 -3
- package/dist/domains/search/services/vector-search/vector-search-facade.js.map +1 -1
- package/dist/domains/search/services/vector-search/vector-search.service.js +1 -1
- package/dist/domains/search/services/vector-search/vector-search.service.js.map +1 -1
- package/dist/server/http-server.d.ts.map +1 -1
- package/dist/server/http-server.js +2 -7
- package/dist/server/http-server.js.map +1 -1
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +33 -7
- package/dist/server/index.js.map +1 -1
- package/dist/server/server-factory.d.ts +65 -0
- package/dist/server/server-factory.d.ts.map +1 -0
- package/dist/server/server-factory.js +40 -0
- package/dist/server/server-factory.js.map +1 -0
- package/dist/server/servers/sse-server.d.ts +33 -0
- package/dist/server/servers/sse-server.d.ts.map +1 -0
- package/dist/server/servers/sse-server.js +48 -0
- package/dist/server/servers/sse-server.js.map +1 -0
- package/dist/server/servers/stdio-server.d.ts +34 -0
- package/dist/server/servers/stdio-server.d.ts.map +1 -0
- package/dist/server/servers/stdio-server.js +58 -0
- package/dist/server/servers/stdio-server.js.map +1 -0
- package/dist/server/simple-mcp-server.d.ts +5 -0
- package/dist/server/simple-mcp-server.d.ts.map +1 -1
- package/dist/server/simple-mcp-server.js +17 -7
- package/dist/server/simple-mcp-server.js.map +1 -1
- package/dist/server/sse-server-impl.d.ts +22 -0
- package/dist/server/sse-server-impl.d.ts.map +1 -0
- package/dist/server/sse-server-impl.js +39 -0
- package/dist/server/sse-server-impl.js.map +1 -0
- package/dist/server/stdio-server-impl.d.ts +12 -0
- package/dist/server/stdio-server-impl.d.ts.map +1 -0
- package/dist/server/stdio-server-impl.js +19 -0
- package/dist/server/stdio-server-impl.js.map +1 -0
- package/dist/shared/types/vector-search.types.d.ts +1 -0
- package/dist/shared/types/vector-search.types.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/__tests__/check-db-integrity.integration.spec.ts +163 -0
- package/scripts/__tests__/fix-migration.integration.spec.ts +203 -0
- package/scripts/__tests__/migrate-embedding-data.integration.spec.ts +219 -0
- package/scripts/__tests__/regenerate-embeddings.integration.spec.ts +192 -0
- package/scripts/backup-embeddings.js +52 -61
- package/scripts/check-db-integrity.js +49 -25
- package/scripts/check-file-sizes.ts +4 -4
- package/scripts/check-pii-masking.ts +0 -3
- package/scripts/check-sql-injection.ts +0 -12
- package/scripts/debug-embeddings.js +74 -93
- package/scripts/fix-migration.js +115 -80
- package/scripts/fix-vector-dimensions.js +70 -89
- package/scripts/migrate-embedding-data.js +111 -25
- package/scripts/regenerate-embeddings.js +31 -15
- package/scripts/run-migration.js +144 -107
- package/scripts/safe-migration.js +192 -142
- package/scripts/save-work-memory.ts +6 -7
- package/scripts/simple-migrate.js +66 -34
- package/scripts/simple-update.js +147 -109
- package/dist/domains/search/algorithms/vector-search-engine-refactored.d.ts +0 -56
- package/dist/domains/search/algorithms/vector-search-engine-refactored.d.ts.map +0 -1
- package/dist/domains/search/algorithms/vector-search-engine-refactored.js +0 -101
- package/dist/domains/search/algorithms/vector-search-engine-refactored.js.map +0 -1
|
@@ -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
|
|
9
|
-
|
|
10
|
-
import {
|
|
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
|
|
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
|
|
75
|
+
console.log(`- ${stat.model}: ${stat.count}개 (평균 ${stat.avg_dim?.toFixed(1) || 'N/A'}차원)`);
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
// 4.
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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
|
-
|
|
112
|
+
WHERE embedding IS NULL OR embedding = '' OR dim IS NULL OR dim = 0
|
|
89
113
|
LIMIT 10
|
|
90
114
|
`).all();
|
|
91
115
|
|
|
92
|
-
|
|
93
|
-
console.log(
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
131
|
+
created_at
|
|
131
132
|
FROM memory_embedding
|
|
132
|
-
|
|
133
|
+
ORDER BY created_at DESC
|
|
134
|
+
LIMIT 5
|
|
133
135
|
`).all();
|
|
134
136
|
|
|
135
|
-
|
|
136
|
-
console.log(
|
|
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
|
-
|
|
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('❌ 디버깅
|
|
144
|
+
console.error('❌ 디버깅 중 오류 발생:', error.message);
|
|
145
|
+
if (error.stack) {
|
|
146
|
+
console.error(' 스택 트레이스:', error.stack);
|
|
147
|
+
}
|
|
173
148
|
process.exit(1);
|
|
174
149
|
} finally {
|
|
175
|
-
|
|
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(
|
|
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 };
|
package/scripts/fix-migration.js
CHANGED
|
@@ -1,93 +1,128 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Memento 마이그레이션 수정 스크립트
|
|
4
|
+
*
|
|
5
|
+
* 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
|
|
6
|
+
*
|
|
7
|
+
* 사용법:
|
|
8
|
+
* - 개발 환경: npx tsx scripts/fix-migration.js
|
|
9
|
+
* - 프로덕션: npm run build && node dist/scripts/fix-migration.js
|
|
10
|
+
*/
|
|
2
11
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const dbPath = join(process.cwd(), 'data', 'memory.db');
|
|
12
|
+
// TypeScript 소스를 직접 import (tsx로 실행 시)
|
|
13
|
+
// 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
|
|
14
|
+
import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
|
|
7
15
|
|
|
8
16
|
console.log('🔧 마이그레이션 수정 중...');
|
|
9
17
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const currentSchema = db.prepare("PRAGMA table_info(memory_embedding)").all();
|
|
16
|
-
console.log('현재 테이블 구조:');
|
|
17
|
-
console.table(currentSchema);
|
|
18
|
-
|
|
19
|
-
// 2. 컬럼이 있는지 확인
|
|
20
|
-
const hasProvider = currentSchema.some(col => col.name === 'embedding_provider');
|
|
21
|
-
const hasDimensions = currentSchema.some(col => col.name === 'dimensions');
|
|
22
|
-
const hasCreatedBy = currentSchema.some(col => col.name === 'created_by');
|
|
23
|
-
|
|
24
|
-
console.log(`embedding_provider: ${hasProvider ? '✅' : '❌'}`);
|
|
25
|
-
console.log(`dimensions: ${hasDimensions ? '✅' : '❌'}`);
|
|
26
|
-
console.log(`created_by: ${hasCreatedBy ? '✅' : '❌'}`);
|
|
18
|
+
/**
|
|
19
|
+
* 마이그레이션 수정 메인 함수
|
|
20
|
+
*/
|
|
21
|
+
async function fixMigration() {
|
|
22
|
+
let db = null;
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const updateResult = db.prepare(`
|
|
33
|
-
UPDATE memory_embedding
|
|
34
|
-
SET
|
|
35
|
-
embedding_provider = CASE
|
|
36
|
-
WHEN model = 'lightweight-hybrid' THEN 'tfidf'
|
|
37
|
-
WHEN model IS NULL OR model = '' THEN 'tfidf'
|
|
38
|
-
ELSE 'unknown'
|
|
39
|
-
END,
|
|
40
|
-
dimensions = dim,
|
|
41
|
-
created_by = 'legacy'
|
|
42
|
-
WHERE embedding_provider IS NULL
|
|
43
|
-
`).run();
|
|
44
|
-
|
|
45
|
-
console.log(`✅ ${updateResult.changes}개 레코드 업데이트 완료`);
|
|
46
|
-
|
|
47
|
-
// 4. 인덱스 추가
|
|
48
|
-
console.log('📝 인덱스 추가 중...');
|
|
49
|
-
db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_provider ON memory_embedding(embedding_provider)');
|
|
50
|
-
db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_dimensions ON memory_embedding(dimensions)');
|
|
51
|
-
db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_created_by ON memory_embedding(created_by)');
|
|
52
|
-
console.log('✅ 인덱스 추가 완료');
|
|
24
|
+
try {
|
|
25
|
+
// 공통 모듈을 사용하여 데이터베이스 초기화
|
|
26
|
+
// initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
|
|
27
|
+
db = await initializeDatabase();
|
|
53
28
|
|
|
54
|
-
//
|
|
55
|
-
console.log('
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
COUNT(CASE WHEN embedding_provider IS NOT NULL THEN 1 END) as with_provider,
|
|
60
|
-
COUNT(CASE WHEN dimensions IS NOT NULL THEN 1 END) as with_dimensions,
|
|
61
|
-
COUNT(CASE WHEN created_by IS NOT NULL THEN 1 END) as with_created_by
|
|
62
|
-
FROM memory_embedding
|
|
63
|
-
`).get();
|
|
29
|
+
// 1. 현재 상태 확인
|
|
30
|
+
console.log('📊 현재 상태 확인...');
|
|
31
|
+
const currentSchema = db.prepare("PRAGMA table_info(memory_embedding)").all();
|
|
32
|
+
console.log('현재 테이블 구조:');
|
|
33
|
+
console.table(currentSchema);
|
|
64
34
|
|
|
65
|
-
|
|
66
|
-
|
|
35
|
+
// 2. 컬럼이 있는지 확인
|
|
36
|
+
const hasProvider = currentSchema.some(col => col.name === 'embedding_provider');
|
|
37
|
+
const hasDimensions = currentSchema.some(col => col.name === 'dimensions');
|
|
38
|
+
const hasCreatedBy = currentSchema.some(col => col.name === 'created_by');
|
|
67
39
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
embedding_provider,
|
|
72
|
-
dimensions,
|
|
73
|
-
COUNT(*) as count
|
|
74
|
-
FROM memory_embedding
|
|
75
|
-
GROUP BY embedding_provider, dimensions
|
|
76
|
-
ORDER BY count DESC
|
|
77
|
-
`).all();
|
|
40
|
+
console.log(`embedding_provider: ${hasProvider ? '✅' : '❌'}`);
|
|
41
|
+
console.log(`dimensions: ${hasDimensions ? '✅' : '❌'}`);
|
|
42
|
+
console.log(`created_by: ${hasCreatedBy ? '✅' : '❌'}`);
|
|
78
43
|
|
|
79
|
-
|
|
80
|
-
|
|
44
|
+
// 3. 데이터 업데이트 (컬럼이 있는 경우에만)
|
|
45
|
+
if (hasProvider && hasDimensions && hasCreatedBy) {
|
|
46
|
+
console.log('🔄 데이터 업데이트 중...');
|
|
47
|
+
|
|
48
|
+
const updateResult = db.prepare(`
|
|
49
|
+
UPDATE memory_embedding
|
|
50
|
+
SET
|
|
51
|
+
embedding_provider = CASE
|
|
52
|
+
WHEN model = 'lightweight-hybrid' THEN 'tfidf'
|
|
53
|
+
WHEN model IS NULL OR model = '' THEN 'tfidf'
|
|
54
|
+
ELSE 'unknown'
|
|
55
|
+
END,
|
|
56
|
+
dimensions = dim,
|
|
57
|
+
created_by = 'legacy'
|
|
58
|
+
WHERE embedding_provider IS NULL
|
|
59
|
+
`).run();
|
|
60
|
+
|
|
61
|
+
console.log(`✅ ${updateResult.changes}개 레코드 업데이트 완료`);
|
|
62
|
+
|
|
63
|
+
// 4. 인덱스 추가
|
|
64
|
+
console.log('📝 인덱스 추가 중...');
|
|
65
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_provider ON memory_embedding(embedding_provider)');
|
|
66
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_dimensions ON memory_embedding(dimensions)');
|
|
67
|
+
db.exec('CREATE INDEX IF NOT EXISTS idx_memory_embedding_created_by ON memory_embedding(created_by)');
|
|
68
|
+
console.log('✅ 인덱스 추가 완료');
|
|
69
|
+
|
|
70
|
+
// 5. 최종 검증
|
|
71
|
+
console.log('🔍 최종 검증...');
|
|
72
|
+
const validation = db.prepare(`
|
|
73
|
+
SELECT
|
|
74
|
+
COUNT(*) as total,
|
|
75
|
+
COUNT(CASE WHEN embedding_provider IS NOT NULL THEN 1 END) as with_provider,
|
|
76
|
+
COUNT(CASE WHEN dimensions IS NOT NULL THEN 1 END) as with_dimensions,
|
|
77
|
+
COUNT(CASE WHEN created_by IS NOT NULL THEN 1 END) as with_created_by
|
|
78
|
+
FROM memory_embedding
|
|
79
|
+
`).get();
|
|
80
|
+
|
|
81
|
+
console.log('📊 검증 결과:');
|
|
82
|
+
console.table(validation);
|
|
83
|
+
|
|
84
|
+
// 6. 최종 데이터 분포
|
|
85
|
+
const finalAnalysis = db.prepare(`
|
|
86
|
+
SELECT
|
|
87
|
+
embedding_provider,
|
|
88
|
+
dimensions,
|
|
89
|
+
COUNT(*) as count
|
|
90
|
+
FROM memory_embedding
|
|
91
|
+
GROUP BY embedding_provider, dimensions
|
|
92
|
+
ORDER BY count DESC
|
|
93
|
+
`).all();
|
|
94
|
+
|
|
95
|
+
console.log('\n📊 최종 데이터 분포:');
|
|
96
|
+
console.table(finalAnalysis);
|
|
97
|
+
|
|
98
|
+
console.log('\n🎉 마이그레이션 완료!');
|
|
99
|
+
|
|
100
|
+
} else {
|
|
101
|
+
console.log('❌ 필요한 컬럼이 없습니다. 스키마를 먼저 업데이트해주세요.');
|
|
102
|
+
console.log(' initializeDatabase가 자동으로 스키마를 생성하지만, 필요한 컬럼이 없을 수 있습니다.');
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
81
105
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
106
|
+
} catch (error) {
|
|
107
|
+
console.error('❌ 오류 발생:', error.message);
|
|
108
|
+
if (error.stack) {
|
|
109
|
+
console.error(' 스택 트레이스:', error.stack);
|
|
110
|
+
}
|
|
111
|
+
process.exit(1);
|
|
112
|
+
} finally {
|
|
113
|
+
// 데이터베이스 연결 종료
|
|
114
|
+
if (db) {
|
|
115
|
+
closeDatabase(db);
|
|
116
|
+
}
|
|
86
117
|
}
|
|
87
|
-
|
|
88
|
-
db.close();
|
|
89
|
-
|
|
90
|
-
} catch (error) {
|
|
91
|
-
console.error('❌ 오류 발생:', error.message);
|
|
92
|
-
process.exit(1);
|
|
93
118
|
}
|
|
119
|
+
|
|
120
|
+
// 스크립트가 직접 실행될 때만 main 함수 호출
|
|
121
|
+
if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
|
|
122
|
+
fixMigration().catch((error) => {
|
|
123
|
+
console.error('❌ 스크립트 실행 중 오류 발생:', error);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export { fixMigration };
|
|
@@ -3,27 +3,31 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* 벡터 차원 통일 스크립트
|
|
5
5
|
* 모든 임베딩을 삭제하고 현재 설정에 맞는 모델로 재생성
|
|
6
|
+
*
|
|
7
|
+
* 리팩토링: 공통 모듈(initializeDatabase)을 사용하여 일관된 DB 초기화 보장
|
|
8
|
+
*
|
|
9
|
+
* 사용법:
|
|
10
|
+
* - 개발 환경: npx tsx scripts/fix-vector-dimensions.js
|
|
11
|
+
* - 프로덕션: npm run build && node dist/scripts/fix-vector-dimensions.js
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
import {
|
|
14
|
+
// TypeScript 소스를 직접 import (tsx로 실행 시)
|
|
15
|
+
// 빌드된 파일을 사용하려면 '../dist/infrastructure/database/database/init.js'로 변경
|
|
16
|
+
import { initializeDatabase, closeDatabase } from '../src/infrastructure/database/database/init.js';
|
|
11
17
|
import { EmbeddingService } from '../dist/services/embedding-service.js';
|
|
12
18
|
|
|
13
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
-
const __dirname = path.dirname(__filename);
|
|
15
|
-
|
|
16
|
-
// 데이터베이스 경로 설정
|
|
17
|
-
const dbPath = process.env.DB_PATH || path.join(__dirname, '..', 'data', 'memory.db');
|
|
18
|
-
|
|
19
19
|
async function fixVectorDimensions() {
|
|
20
20
|
console.log('🔧 벡터 차원 통일 작업 시작...');
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
const db = new Database(dbPath);
|
|
24
|
-
const embeddingService = new EmbeddingService();
|
|
22
|
+
let db = null;
|
|
25
23
|
|
|
26
24
|
try {
|
|
25
|
+
// 공통 모듈을 사용하여 데이터베이스 초기화
|
|
26
|
+
// initializeDatabase는 DB 파일이 없으면 자동으로 생성하고 초기화함
|
|
27
|
+
db = await initializeDatabase();
|
|
28
|
+
|
|
29
|
+
const embeddingService = new EmbeddingService();
|
|
30
|
+
|
|
27
31
|
// 1. 현재 상태 확인
|
|
28
32
|
console.log('\n📊 현재 상태:');
|
|
29
33
|
const currentStats = db.prepare(`
|
|
@@ -50,59 +54,39 @@ async function fixVectorDimensions() {
|
|
|
50
54
|
process.exit(1);
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
// 3.
|
|
54
|
-
console.log('\n
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
me.memory_id,
|
|
58
|
-
me.embedding,
|
|
59
|
-
me.dim,
|
|
60
|
-
me.model,
|
|
61
|
-
me.created_at,
|
|
62
|
-
mi.content,
|
|
63
|
-
mi.type
|
|
64
|
-
FROM memory_embedding me
|
|
65
|
-
JOIN memory_item mi ON me.memory_id = mi.id
|
|
66
|
-
`).all();
|
|
57
|
+
// 3. 사용자 확인
|
|
58
|
+
console.log('\n⚠️ 경고: 이 작업은 모든 임베딩을 삭제하고 재생성합니다.');
|
|
59
|
+
console.log('백업을 먼저 수행하는 것을 권장합니다.');
|
|
60
|
+
console.log('계속하려면 스크립트를 --confirm 플래그와 함께 실행하세요.');
|
|
67
61
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
fs.mkdirSync(path.dirname(backupFile), { recursive: true });
|
|
62
|
+
if (!process.argv.includes('--confirm')) {
|
|
63
|
+
console.log('\n❌ 확인 플래그가 없어 작업을 중단합니다.');
|
|
64
|
+
console.log('사용법: npx tsx scripts/fix-vector-dimensions.js --confirm');
|
|
65
|
+
return;
|
|
73
66
|
}
|
|
74
67
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
totalEmbeddings: backupData.length,
|
|
78
|
-
embeddings: backupData.map(emb => ({
|
|
79
|
-
memory_id: emb.memory_id,
|
|
80
|
-
content: emb.content,
|
|
81
|
-
type: emb.type,
|
|
82
|
-
embedding: JSON.parse(emb.embedding),
|
|
83
|
-
dim: emb.dim,
|
|
84
|
-
model: emb.model,
|
|
85
|
-
created_at: emb.created_at
|
|
86
|
-
}))
|
|
87
|
-
}, null, 2));
|
|
88
|
-
|
|
89
|
-
console.log(`✅ 백업 완료: ${backupFile}`);
|
|
90
|
-
|
|
91
|
-
// 4. 기존 임베딩 삭제
|
|
92
|
-
console.log('\n🗑️ 기존 임베딩 삭제 중...');
|
|
93
|
-
const deleteResult = db.prepare('DELETE FROM memory_embedding').run();
|
|
94
|
-
console.log(`✅ 삭제 완료: ${deleteResult.changes}개 행 삭제`);
|
|
95
|
-
|
|
96
|
-
// 5. 모든 메모리에 대해 새로운 임베딩 생성
|
|
97
|
-
console.log('\n🔄 새로운 임베딩 생성 중...');
|
|
68
|
+
// 4. 모든 기억 조회
|
|
69
|
+
console.log('\n📋 기억 조회 중...');
|
|
98
70
|
const memories = db.prepare(`
|
|
99
|
-
SELECT id, content, type
|
|
71
|
+
SELECT id, content, type
|
|
100
72
|
FROM memory_item
|
|
101
73
|
ORDER BY created_at
|
|
102
74
|
`).all();
|
|
103
75
|
|
|
104
|
-
console.log(`📊 처리할
|
|
76
|
+
console.log(`📊 처리할 기억 개수: ${memories.length}`);
|
|
77
|
+
|
|
78
|
+
if (memories.length === 0) {
|
|
79
|
+
console.log('⚠️ 재생성할 기억이 없습니다.');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
105
82
|
|
|
83
|
+
// 5. 기존 임베딩 삭제
|
|
84
|
+
console.log('\n🗑️ 기존 임베딩 삭제 중...');
|
|
85
|
+
const deleteResult = db.prepare('DELETE FROM memory_embedding').run();
|
|
86
|
+
console.log(`✅ ${deleteResult.changes}개 임베딩 삭제 완료`);
|
|
87
|
+
|
|
88
|
+
// 6. 새로운 임베딩 생성
|
|
89
|
+
console.log('\n🔄 새로운 임베딩 생성 중...');
|
|
106
90
|
let successCount = 0;
|
|
107
91
|
let errorCount = 0;
|
|
108
92
|
|
|
@@ -136,7 +120,7 @@ async function fixVectorDimensions() {
|
|
|
136
120
|
console.log(`${progress} ✅ 완료: ${memory.id} (${embeddingResult.embedding.length}차원)`);
|
|
137
121
|
successCount++;
|
|
138
122
|
|
|
139
|
-
// API 제한을 위한 대기
|
|
123
|
+
// API 제한을 위한 대기 (필요시)
|
|
140
124
|
if (i % 10 === 0 && i > 0) {
|
|
141
125
|
console.log('⏳ API 제한 대기 중...');
|
|
142
126
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
@@ -144,63 +128,60 @@ async function fixVectorDimensions() {
|
|
|
144
128
|
|
|
145
129
|
} catch (error) {
|
|
146
130
|
console.error(`${progress} ❌ 오류: ${memory.id}`, error.message);
|
|
131
|
+
if (error.stack) {
|
|
132
|
+
console.error(' 스택 트레이스:', error.stack);
|
|
133
|
+
}
|
|
147
134
|
errorCount++;
|
|
148
135
|
}
|
|
149
136
|
}
|
|
150
137
|
|
|
151
|
-
//
|
|
152
|
-
console.log('\n
|
|
138
|
+
// 7. 결과 통계
|
|
139
|
+
console.log('\n📊 작업 완료!');
|
|
140
|
+
console.log(`✅ 성공: ${successCount}개`);
|
|
141
|
+
console.log(`❌ 실패: ${errorCount}개`);
|
|
142
|
+
console.log(`📈 성공률: ${((successCount / memories.length) * 100).toFixed(1)}%`);
|
|
143
|
+
|
|
144
|
+
// 8. 최종 검증
|
|
153
145
|
const finalStats = db.prepare(`
|
|
154
146
|
SELECT
|
|
155
147
|
COUNT(*) as total,
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
MAX(dim) as max_dim,
|
|
159
|
-
COUNT(DISTINCT dim) as unique_dims
|
|
148
|
+
COUNT(DISTINCT dim) as unique_dims,
|
|
149
|
+
AVG(dim) as avg_dim
|
|
160
150
|
FROM memory_embedding
|
|
161
151
|
`).get();
|
|
162
152
|
|
|
153
|
+
console.log('\n🔍 최종 검증:');
|
|
163
154
|
console.log(`- 총 임베딩: ${finalStats.total}개`);
|
|
164
|
-
console.log(`- 평균 차원: ${finalStats.avg_dim?.toFixed(1) || 'N/A'}`);
|
|
165
|
-
console.log(`- 최소 차원: ${finalStats.min_dim || 'N/A'}`);
|
|
166
|
-
console.log(`- 최대 차원: ${finalStats.max_dim || 'N/A'}`);
|
|
167
155
|
console.log(`- 고유 차원 수: ${finalStats.unique_dims}개`);
|
|
156
|
+
console.log(`- 평균 차원: ${finalStats.avg_dim?.toFixed(1) || 'N/A'}`);
|
|
168
157
|
|
|
169
|
-
// 차원 일치성 확인
|
|
170
158
|
const expectedDim = modelInfo.dimensions;
|
|
171
|
-
|
|
172
|
-
SELECT COUNT(*) as count FROM memory_embedding WHERE dim != ?
|
|
173
|
-
`).get(expectedDim);
|
|
174
|
-
|
|
175
|
-
if (mismatchedDims.count > 0) {
|
|
176
|
-
console.warn(`⚠️ 차원 불일치 발견: ${mismatchedDims.count}개`);
|
|
177
|
-
} else {
|
|
159
|
+
if (finalStats.unique_dims === 1 && finalStats.avg_dim === expectedDim) {
|
|
178
160
|
console.log('✅ 모든 임베딩의 차원이 일치합니다!');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// 7. 결과 요약
|
|
182
|
-
console.log('\n📊 작업 완료 요약:');
|
|
183
|
-
console.log(`✅ 성공: ${successCount}개`);
|
|
184
|
-
console.log(`❌ 실패: ${errorCount}개`);
|
|
185
|
-
console.log(`📈 성공률: ${((successCount / memories.length) * 100).toFixed(1)}%`);
|
|
186
|
-
|
|
187
|
-
if (finalStats.unique_dims === 1) {
|
|
188
|
-
console.log('🎉 벡터 차원 통일 완료! 이제 검색이 정상 작동할 것입니다.');
|
|
189
161
|
} else {
|
|
190
|
-
console.
|
|
162
|
+
console.warn(`⚠️ 차원 불일치: 예상 ${expectedDim}차원, 실제 ${finalStats.avg_dim?.toFixed(1) || 'N/A'}차원`);
|
|
191
163
|
}
|
|
192
164
|
|
|
193
165
|
} catch (error) {
|
|
194
|
-
console.error('❌
|
|
166
|
+
console.error('❌ 작업 실패:', error.message);
|
|
167
|
+
if (error.stack) {
|
|
168
|
+
console.error(' 스택 트레이스:', error.stack);
|
|
169
|
+
}
|
|
195
170
|
process.exit(1);
|
|
196
171
|
} finally {
|
|
197
|
-
|
|
172
|
+
// 데이터베이스 연결 종료
|
|
173
|
+
if (db) {
|
|
174
|
+
closeDatabase(db);
|
|
175
|
+
}
|
|
198
176
|
}
|
|
199
177
|
}
|
|
200
178
|
|
|
201
179
|
// 스크립트 실행
|
|
202
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
203
|
-
fixVectorDimensions().catch(
|
|
180
|
+
if (import.meta.url === `file://${process.argv[1]}` || import.meta.url.endsWith(process.argv[1])) {
|
|
181
|
+
fixVectorDimensions().catch((error) => {
|
|
182
|
+
console.error('❌ 스크립트 실행 중 오류 발생:', error);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
});
|
|
204
185
|
}
|
|
205
186
|
|
|
206
187
|
export { fixVectorDimensions };
|