memento-mcp-server 1.14.0-b → 1.15.0-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/memory/tools/convert-episodic-to-semantic-tool.d.ts +18 -0
- package/dist/domains/memory/tools/convert-episodic-to-semantic-tool.d.ts.map +1 -0
- package/dist/domains/memory/tools/convert-episodic-to-semantic-tool.js +346 -0
- package/dist/domains/memory/tools/convert-episodic-to-semantic-tool.js.map +1 -0
- package/dist/domains/memory/tools/recall-tool.d.ts +177 -0
- package/dist/domains/memory/tools/recall-tool.d.ts.map +1 -1
- package/dist/domains/memory/tools/recall-tool.js +329 -3
- package/dist/domains/memory/tools/recall-tool.js.map +1 -1
- package/dist/domains/memory/tools/remember-tool.d.ts.map +1 -1
- package/dist/domains/memory/tools/remember-tool.js +182 -1
- package/dist/domains/memory/tools/remember-tool.js.map +1 -1
- package/dist/infrastructure/database/database/migration/migrations/005-relation-engine-schema.sql +7 -7
- package/dist/infrastructure/database/database/migration/migrations/008-arigraph-schema-expansion.d.ts +65 -0
- package/dist/infrastructure/database/database/migration/migrations/008-arigraph-schema-expansion.d.ts.map +1 -0
- package/dist/infrastructure/database/database/migration/migrations/008-arigraph-schema-expansion.js +250 -0
- package/dist/infrastructure/database/database/migration/migrations/008-arigraph-schema-expansion.js.map +1 -0
- package/dist/infrastructure/database/database/migration/migrations/008-arigraph-schema-expansion.sql +86 -0
- package/dist/infrastructure/logging/triple-extraction-logger.d.ts +92 -0
- package/dist/infrastructure/logging/triple-extraction-logger.d.ts.map +1 -0
- package/dist/infrastructure/logging/triple-extraction-logger.js +194 -0
- package/dist/infrastructure/logging/triple-extraction-logger.js.map +1 -0
- package/dist/infrastructure/scheduler/batch-scheduler.d.ts +57 -0
- package/dist/infrastructure/scheduler/batch-scheduler.d.ts.map +1 -1
- package/dist/infrastructure/scheduler/batch-scheduler.js +220 -0
- package/dist/infrastructure/scheduler/batch-scheduler.js.map +1 -1
- package/dist/infrastructure/scheduler/jobs/triple-extraction-batch-job.d.ts +194 -0
- package/dist/infrastructure/scheduler/jobs/triple-extraction-batch-job.d.ts.map +1 -0
- package/dist/infrastructure/scheduler/jobs/triple-extraction-batch-job.js +639 -0
- package/dist/infrastructure/scheduler/jobs/triple-extraction-batch-job.js.map +1 -0
- package/dist/services/semantic-memory/semantic-memory-statistics.d.ts +64 -0
- package/dist/services/semantic-memory/semantic-memory-statistics.d.ts.map +1 -0
- package/dist/services/semantic-memory/semantic-memory-statistics.js +113 -0
- package/dist/services/semantic-memory/semantic-memory-statistics.js.map +1 -0
- package/dist/services/semantic-memory/semantic-memory-update-service.d.ts +257 -0
- package/dist/services/semantic-memory/semantic-memory-update-service.d.ts.map +1 -0
- package/dist/services/semantic-memory/semantic-memory-update-service.js +696 -0
- package/dist/services/semantic-memory/semantic-memory-update-service.js.map +1 -0
- package/dist/services/triple-extraction/entity-linker.d.ts +55 -0
- package/dist/services/triple-extraction/entity-linker.d.ts.map +1 -0
- package/dist/services/triple-extraction/entity-linker.js +154 -0
- package/dist/services/triple-extraction/entity-linker.js.map +1 -0
- package/dist/services/triple-extraction/predicate-canonicalizer.d.ts +63 -0
- package/dist/services/triple-extraction/predicate-canonicalizer.d.ts.map +1 -0
- package/dist/services/triple-extraction/predicate-canonicalizer.js +166 -0
- package/dist/services/triple-extraction/predicate-canonicalizer.js.map +1 -0
- package/dist/services/triple-extraction/triple-extraction-service.d.ts +181 -0
- package/dist/services/triple-extraction/triple-extraction-service.d.ts.map +1 -0
- package/dist/services/triple-extraction/triple-extraction-service.js +907 -0
- package/dist/services/triple-extraction/triple-extraction-service.js.map +1 -0
- package/dist/services/triple-extraction/triple-extraction-statistics.d.ts +74 -0
- package/dist/services/triple-extraction/triple-extraction-statistics.d.ts.map +1 -0
- package/dist/services/triple-extraction/triple-extraction-statistics.js +146 -0
- package/dist/services/triple-extraction/triple-extraction-statistics.js.map +1 -0
- package/dist/shared/types/index.d.ts +1 -0
- package/dist/shared/types/index.d.ts.map +1 -1
- package/dist/shared/types/index.js.map +1 -1
- package/dist/shared/types/triple-extraction.d.ts +99 -0
- package/dist/shared/types/triple-extraction.d.ts.map +1 -0
- package/dist/shared/types/triple-extraction.js +6 -0
- package/dist/shared/types/triple-extraction.js.map +1 -0
- package/dist/shared/utils/pii-masker.d.ts +67 -0
- package/dist/shared/utils/pii-masker.d.ts.map +1 -0
- package/dist/shared/utils/pii-masker.js +205 -0
- package/dist/shared/utils/pii-masker.js.map +1 -0
- package/dist/shared/utils/prompt-template-loader.d.ts +42 -0
- package/dist/shared/utils/prompt-template-loader.d.ts.map +1 -0
- package/dist/shared/utils/prompt-template-loader.js +92 -0
- package/dist/shared/utils/prompt-template-loader.js.map +1 -0
- package/dist/shared/utils/triple-cache.d.ts +90 -0
- package/dist/shared/utils/triple-cache.d.ts.map +1 -0
- package/dist/shared/utils/triple-cache.js +124 -0
- package/dist/shared/utils/triple-cache.js.map +1 -0
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/types.d.ts +1 -0
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -1
- package/package.json +1 -1
package/dist/infrastructure/database/database/migration/migrations/008-arigraph-schema-expansion.js
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Migration: 008 - AriGraph Schema Expansion
|
|
3
|
+
* Description: Add triple extraction fields to memory_item table for AriGraph pipeline
|
|
4
|
+
* Version: 8.0
|
|
5
|
+
* Date: 2025-01-XX
|
|
6
|
+
*/
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { join, dirname } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { DependencyValidator } from '../dependency-validator.js';
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
/**
|
|
14
|
+
* AriGraph Schema Expansion Migration
|
|
15
|
+
*
|
|
16
|
+
* This migration:
|
|
17
|
+
* 1. Adds subject, predicate, object columns to memory_item table (for semantic memory structural storage)
|
|
18
|
+
* 2. Adds triple_extracted, triple_extracted_status, triple_extraction_metadata columns to memory_item table
|
|
19
|
+
* 3. Creates indexes for triple extraction fields
|
|
20
|
+
* 4. Inserts initial relation types (extracted_from, supported_by) into relation_type_registry
|
|
21
|
+
*/
|
|
22
|
+
export class AriGraphSchemaExpansionMigration {
|
|
23
|
+
version = '8.0';
|
|
24
|
+
name = 'arigraph-schema-expansion';
|
|
25
|
+
description = 'Add triple extraction fields to memory_item table for AriGraph pipeline';
|
|
26
|
+
/**
|
|
27
|
+
* Load SQL file content
|
|
28
|
+
*/
|
|
29
|
+
loadSQLFile(filename) {
|
|
30
|
+
const filePath = join(__dirname, filename);
|
|
31
|
+
return readFileSync(filePath, 'utf-8');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Execute SQL script
|
|
35
|
+
* Removes transaction commands (BEGIN TRANSACTION, COMMIT) as MigrationRunner manages transactions
|
|
36
|
+
*/
|
|
37
|
+
executeSQL(db, sql) {
|
|
38
|
+
// MigrationRunner가 트랜잭션을 관리하므로 SQL에서 트랜잭션 명령 제거
|
|
39
|
+
let cleanedSQL = sql
|
|
40
|
+
// BEGIN TRANSACTION 제거
|
|
41
|
+
.replace(/BEGIN\s+TRANSACTION\s*;/gi, '')
|
|
42
|
+
// COMMIT 제거
|
|
43
|
+
.replace(/COMMIT\s*;/gi, '')
|
|
44
|
+
// PRAGMA foreign_keys 명령은 유지 (트랜잭션 외부에서도 작동)
|
|
45
|
+
.trim();
|
|
46
|
+
// 빈 줄 제거 및 정리
|
|
47
|
+
cleanedSQL = cleanedSQL
|
|
48
|
+
.split('\n')
|
|
49
|
+
.map(line => line.trim())
|
|
50
|
+
.filter(line => line.length > 0)
|
|
51
|
+
.join('\n');
|
|
52
|
+
if (cleanedSQL.length > 0) {
|
|
53
|
+
db.exec(cleanedSQL);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Check if table exists
|
|
58
|
+
*/
|
|
59
|
+
tableExists(db, tableName) {
|
|
60
|
+
const result = db.prepare(`
|
|
61
|
+
SELECT name FROM sqlite_master
|
|
62
|
+
WHERE type='table' AND name=?
|
|
63
|
+
`).get(tableName);
|
|
64
|
+
return !!result;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if column exists in table
|
|
68
|
+
*/
|
|
69
|
+
columnExists(db, tableName, columnName) {
|
|
70
|
+
const columns = db.prepare(`PRAGMA table_info(${tableName})`).all();
|
|
71
|
+
return columns.some(col => col.name === columnName);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check if index exists
|
|
75
|
+
*/
|
|
76
|
+
indexExists(db, indexName) {
|
|
77
|
+
const result = db.prepare(`
|
|
78
|
+
SELECT name FROM sqlite_master
|
|
79
|
+
WHERE type='index' AND name=?
|
|
80
|
+
`).get(indexName);
|
|
81
|
+
return !!result;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Validate before migration
|
|
85
|
+
*/
|
|
86
|
+
async validateBefore(db) {
|
|
87
|
+
// Check if memory_item table exists (required for ALTER TABLE)
|
|
88
|
+
if (!this.tableExists(db, 'memory_item')) {
|
|
89
|
+
throw new Error('memory_item table does not exist. Cannot proceed with migration.');
|
|
90
|
+
}
|
|
91
|
+
// Check if relation_type_registry table exists (required for INSERT)
|
|
92
|
+
if (!this.tableExists(db, 'relation_type_registry')) {
|
|
93
|
+
throw new Error('relation_type_registry table does not exist. Cannot proceed with migration.');
|
|
94
|
+
}
|
|
95
|
+
// Check if migration has already been applied
|
|
96
|
+
if (this.tableExists(db, 'memento_schema_version')) {
|
|
97
|
+
const version = db.prepare(`
|
|
98
|
+
SELECT version FROM memento_schema_version WHERE version = ?
|
|
99
|
+
`).get('8.0');
|
|
100
|
+
if (version) {
|
|
101
|
+
throw new Error('Migration 008 has already been applied. Current schema version: 8.0');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Check if new columns already exist (should not exist)
|
|
105
|
+
if (this.columnExists(db, 'memory_item', 'subject')) {
|
|
106
|
+
throw new Error('subject column already exists in memory_item table. Migration may have been partially applied.');
|
|
107
|
+
}
|
|
108
|
+
if (this.columnExists(db, 'memory_item', 'predicate')) {
|
|
109
|
+
throw new Error('predicate column already exists in memory_item table. Migration may have been partially applied.');
|
|
110
|
+
}
|
|
111
|
+
if (this.columnExists(db, 'memory_item', 'object')) {
|
|
112
|
+
throw new Error('object column already exists in memory_item table. Migration may have been partially applied.');
|
|
113
|
+
}
|
|
114
|
+
if (this.columnExists(db, 'memory_item', 'triple_extracted')) {
|
|
115
|
+
throw new Error('triple_extracted column already exists in memory_item table. Migration may have been partially applied.');
|
|
116
|
+
}
|
|
117
|
+
if (this.columnExists(db, 'memory_item', 'triple_extracted_status')) {
|
|
118
|
+
throw new Error('triple_extracted_status column already exists in memory_item table. Migration may have been partially applied.');
|
|
119
|
+
}
|
|
120
|
+
if (this.columnExists(db, 'memory_item', 'triple_extraction_metadata')) {
|
|
121
|
+
throw new Error('triple_extraction_metadata column already exists in memory_item table. Migration may have been partially applied.');
|
|
122
|
+
}
|
|
123
|
+
// Check if new indexes already exist (should not exist)
|
|
124
|
+
if (this.indexExists(db, 'idx_memory_item_triple')) {
|
|
125
|
+
throw new Error('idx_memory_item_triple index already exists. Migration may have been partially applied.');
|
|
126
|
+
}
|
|
127
|
+
if (this.indexExists(db, 'idx_memory_item_triple_extracted')) {
|
|
128
|
+
throw new Error('idx_memory_item_triple_extracted index already exists. Migration may have been partially applied.');
|
|
129
|
+
}
|
|
130
|
+
if (this.indexExists(db, 'idx_memory_item_triple_status')) {
|
|
131
|
+
throw new Error('idx_memory_item_triple_status index already exists. Migration may have been partially applied.');
|
|
132
|
+
}
|
|
133
|
+
// Check if relation types already exist (should not exist, but use INSERT OR IGNORE in SQL)
|
|
134
|
+
const existingExtractedFrom = db.prepare(`
|
|
135
|
+
SELECT type_name FROM relation_type_registry WHERE type_name = ?
|
|
136
|
+
`).get('extracted_from');
|
|
137
|
+
const existingSupportedBy = db.prepare(`
|
|
138
|
+
SELECT type_name FROM relation_type_registry WHERE type_name = ?
|
|
139
|
+
`).get('supported_by');
|
|
140
|
+
// Note: We allow existing relation types (INSERT OR IGNORE in SQL), so we don't throw here
|
|
141
|
+
// This allows the migration to be idempotent
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Execute migration (Up)
|
|
145
|
+
*/
|
|
146
|
+
async up(db) {
|
|
147
|
+
// Load and execute SQL script
|
|
148
|
+
const sqlScript = this.loadSQLFile('008-arigraph-schema-expansion.sql');
|
|
149
|
+
this.executeSQL(db, sqlScript);
|
|
150
|
+
// Note: Schema version is recorded by MigrationRunner, not here
|
|
151
|
+
// MigrationRunner.recordVersion() will be called after successful migration
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Rollback migration (Down)
|
|
155
|
+
*
|
|
156
|
+
* Note: SQLite does not support ALTER TABLE DROP COLUMN directly (requires SQLite 3.35.0+).
|
|
157
|
+
* For full rollback, table recreation would be required, which is complex.
|
|
158
|
+
* This implementation provides a basic rollback that removes indexes.
|
|
159
|
+
* Columns cannot be easily removed without table recreation.
|
|
160
|
+
*/
|
|
161
|
+
async down(db) {
|
|
162
|
+
// Drop indexes first
|
|
163
|
+
const indexes = [
|
|
164
|
+
'idx_memory_item_triple',
|
|
165
|
+
'idx_memory_item_triple_extracted',
|
|
166
|
+
'idx_memory_item_triple_status'
|
|
167
|
+
];
|
|
168
|
+
for (const indexName of indexes) {
|
|
169
|
+
db.exec(`DROP INDEX IF EXISTS ${indexName}`);
|
|
170
|
+
}
|
|
171
|
+
// Remove relation types from registry
|
|
172
|
+
db.prepare('DELETE FROM relation_type_registry WHERE type_name = ?').run('extracted_from');
|
|
173
|
+
db.prepare('DELETE FROM relation_type_registry WHERE type_name = ?').run('supported_by');
|
|
174
|
+
// Note: Removing columns from memory_item requires table recreation, which is complex.
|
|
175
|
+
// For production use, consider using a new migration to remove columns if needed.
|
|
176
|
+
// SQLite 3.35.0+ supports ALTER TABLE DROP COLUMN, but for compatibility, we skip it here.
|
|
177
|
+
// Remove schema version record
|
|
178
|
+
db.prepare('DELETE FROM memento_schema_version WHERE version = ?').run('8.0');
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Validate after migration
|
|
182
|
+
*/
|
|
183
|
+
async validateAfter(db) {
|
|
184
|
+
// Verify memory_item table has new columns
|
|
185
|
+
if (!this.columnExists(db, 'memory_item', 'subject')) {
|
|
186
|
+
throw new Error('subject column was not added to memory_item table');
|
|
187
|
+
}
|
|
188
|
+
if (!this.columnExists(db, 'memory_item', 'predicate')) {
|
|
189
|
+
throw new Error('predicate column was not added to memory_item table');
|
|
190
|
+
}
|
|
191
|
+
if (!this.columnExists(db, 'memory_item', 'object')) {
|
|
192
|
+
throw new Error('object column was not added to memory_item table');
|
|
193
|
+
}
|
|
194
|
+
if (!this.columnExists(db, 'memory_item', 'triple_extracted')) {
|
|
195
|
+
throw new Error('triple_extracted column was not added to memory_item table');
|
|
196
|
+
}
|
|
197
|
+
if (!this.columnExists(db, 'memory_item', 'triple_extracted_status')) {
|
|
198
|
+
throw new Error('triple_extracted_status column was not added to memory_item table');
|
|
199
|
+
}
|
|
200
|
+
if (!this.columnExists(db, 'memory_item', 'triple_extraction_metadata')) {
|
|
201
|
+
throw new Error('triple_extraction_metadata column was not added to memory_item table');
|
|
202
|
+
}
|
|
203
|
+
// Verify indexes were created
|
|
204
|
+
if (!this.indexExists(db, 'idx_memory_item_triple')) {
|
|
205
|
+
throw new Error('idx_memory_item_triple index was not created');
|
|
206
|
+
}
|
|
207
|
+
if (!this.indexExists(db, 'idx_memory_item_triple_extracted')) {
|
|
208
|
+
throw new Error('idx_memory_item_triple_extracted index was not created');
|
|
209
|
+
}
|
|
210
|
+
if (!this.indexExists(db, 'idx_memory_item_triple_status')) {
|
|
211
|
+
throw new Error('idx_memory_item_triple_status index was not created');
|
|
212
|
+
}
|
|
213
|
+
// Verify relation types were inserted (or already exist)
|
|
214
|
+
const extractedFrom = db.prepare(`
|
|
215
|
+
SELECT type_name FROM relation_type_registry WHERE type_name = ?
|
|
216
|
+
`).get('extracted_from');
|
|
217
|
+
if (!extractedFrom) {
|
|
218
|
+
throw new Error('extracted_from relation type was not inserted into relation_type_registry');
|
|
219
|
+
}
|
|
220
|
+
const supportedBy = db.prepare(`
|
|
221
|
+
SELECT type_name FROM relation_type_registry WHERE type_name = ?
|
|
222
|
+
`).get('supported_by');
|
|
223
|
+
if (!supportedBy) {
|
|
224
|
+
throw new Error('supported_by relation type was not inserted into relation_type_registry');
|
|
225
|
+
}
|
|
226
|
+
// Verify relation type metadata
|
|
227
|
+
const extractedFromInfo = db.prepare(`
|
|
228
|
+
SELECT category, description FROM relation_type_registry WHERE type_name = ?
|
|
229
|
+
`).get('extracted_from');
|
|
230
|
+
if (!extractedFromInfo || extractedFromInfo.category !== 'Structural') {
|
|
231
|
+
throw new Error('extracted_from relation type has incorrect category or metadata');
|
|
232
|
+
}
|
|
233
|
+
const supportedByInfo = db.prepare(`
|
|
234
|
+
SELECT category, description FROM relation_type_registry WHERE type_name = ?
|
|
235
|
+
`).get('supported_by');
|
|
236
|
+
if (!supportedByInfo || supportedByInfo.category !== 'Structural') {
|
|
237
|
+
throw new Error('supported_by relation type has incorrect category or metadata');
|
|
238
|
+
}
|
|
239
|
+
// Verify existing dependencies are intact using DependencyValidator
|
|
240
|
+
const dependencyReport = await DependencyValidator.validateAll(db);
|
|
241
|
+
if (!dependencyReport.success) {
|
|
242
|
+
const errors = dependencyReport.results
|
|
243
|
+
.filter(r => !r.success)
|
|
244
|
+
.map(r => `${r.name}: ${r.error}`)
|
|
245
|
+
.join('; ');
|
|
246
|
+
throw new Error(`Dependency validation failed: ${errors}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
//# sourceMappingURL=008-arigraph-schema-expansion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"008-arigraph-schema-expansion.js","sourceRoot":"","sources":["../../../../../../src/infrastructure/database/database/migration/migrations/008-arigraph-schema-expansion.ts"],"names":[],"mappings":"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"}
|
package/dist/infrastructure/database/database/migration/migrations/008-arigraph-schema-expansion.sql
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
-- Migration: 008 - AriGraph Schema Expansion
|
|
2
|
+
-- Description: Add triple extraction fields to memory_item table for AriGraph pipeline
|
|
3
|
+
-- Version: 8.0
|
|
4
|
+
-- Date: 2025-01-XX
|
|
5
|
+
--
|
|
6
|
+
-- This migration:
|
|
7
|
+
-- 1. Adds subject, predicate, object columns to memory_item table (for semantic memory structural storage)
|
|
8
|
+
-- 2. Adds triple_extracted, triple_extracted_status, triple_extraction_metadata columns to memory_item table
|
|
9
|
+
-- 3. Creates indexes for triple extraction fields
|
|
10
|
+
-- 4. Inserts initial relation types (extracted_from, supported_by) into relation_type_registry
|
|
11
|
+
|
|
12
|
+
-- ============================================================================
|
|
13
|
+
-- 1. Add triple structure columns to memory_item table (for semantic memory)
|
|
14
|
+
-- ============================================================================
|
|
15
|
+
|
|
16
|
+
-- Add subject column (TEXT, NULL allowed, for semantic memory only)
|
|
17
|
+
ALTER TABLE memory_item ADD COLUMN subject TEXT;
|
|
18
|
+
|
|
19
|
+
-- Add predicate column (TEXT, NULL allowed, for semantic memory only)
|
|
20
|
+
ALTER TABLE memory_item ADD COLUMN predicate TEXT;
|
|
21
|
+
|
|
22
|
+
-- Add object column (TEXT, NULL allowed, for semantic memory only)
|
|
23
|
+
ALTER TABLE memory_item ADD COLUMN object TEXT;
|
|
24
|
+
|
|
25
|
+
-- ============================================================================
|
|
26
|
+
-- 2. Add triple extraction tracking columns to memory_item table
|
|
27
|
+
-- ============================================================================
|
|
28
|
+
|
|
29
|
+
-- Add triple_extracted column (BOOLEAN, NULL allowed, default NULL)
|
|
30
|
+
-- NULL: 미처리, TRUE: 성공, FALSE: 실패 또는 미처리
|
|
31
|
+
ALTER TABLE memory_item ADD COLUMN triple_extracted BOOLEAN DEFAULT NULL;
|
|
32
|
+
|
|
33
|
+
-- Add triple_extracted_status column (TEXT, NULL allowed, default NULL)
|
|
34
|
+
-- NULL: 미처리, 'success': 성공, 'failed': 실패, 'abandoned': 포기
|
|
35
|
+
ALTER TABLE memory_item ADD COLUMN triple_extracted_status TEXT DEFAULT NULL;
|
|
36
|
+
|
|
37
|
+
-- Add triple_extraction_metadata column (TEXT, NULL allowed, JSON format, default NULL)
|
|
38
|
+
-- 성공 시: {"triple_count": 3, "confidence_avg": 0.85, "extracted_at": "2025-01-XX"}
|
|
39
|
+
-- 실패 시: {"failureReason": "no_triple", "retry_count": 2, "last_attempt": "2025-01-XX"}
|
|
40
|
+
-- 포기 시: {"failureReason": "llm_api_error", "retry_count": 3, "last_attempt": "2025-01-XX", "abandoned_at": "2025-01-XX"}
|
|
41
|
+
ALTER TABLE memory_item ADD COLUMN triple_extraction_metadata TEXT DEFAULT NULL;
|
|
42
|
+
|
|
43
|
+
-- ============================================================================
|
|
44
|
+
-- 3. Create indexes for triple extraction fields
|
|
45
|
+
-- ============================================================================
|
|
46
|
+
|
|
47
|
+
-- Partial index for triple structure (only for semantic memory with triple data)
|
|
48
|
+
-- This index is used for efficient triple-based queries
|
|
49
|
+
CREATE INDEX IF NOT EXISTS idx_memory_item_triple ON memory_item(subject, predicate, object)
|
|
50
|
+
WHERE type='semantic' AND subject IS NOT NULL AND predicate IS NOT NULL AND object IS NOT NULL;
|
|
51
|
+
|
|
52
|
+
-- Index for triple_extracted field (for batch job queries)
|
|
53
|
+
CREATE INDEX IF NOT EXISTS idx_memory_item_triple_extracted ON memory_item(triple_extracted);
|
|
54
|
+
|
|
55
|
+
-- Index for triple_extracted_status field (for batch job queries and statistics)
|
|
56
|
+
CREATE INDEX IF NOT EXISTS idx_memory_item_triple_status ON memory_item(triple_extracted_status);
|
|
57
|
+
|
|
58
|
+
-- ============================================================================
|
|
59
|
+
-- 4. Insert initial relation types for AriGraph pipeline
|
|
60
|
+
-- ============================================================================
|
|
61
|
+
-- Note: These relation types are used for Episodic-Edge creation
|
|
62
|
+
-- extracted_from: Episodic → Semantic (source: Episodic, target: Semantic)
|
|
63
|
+
-- supported_by: Semantic → Episodic (source: Semantic, target: Episodic)
|
|
64
|
+
|
|
65
|
+
-- Insert extracted_from relation type (Structural category)
|
|
66
|
+
INSERT OR IGNORE INTO relation_type_registry (type_name, category, description, applicable_types, default_confidence, search_boost)
|
|
67
|
+
VALUES (
|
|
68
|
+
'extracted_from',
|
|
69
|
+
'Structural',
|
|
70
|
+
'추출 관계: Semantic Memory가 Episodic Memory에서 추출됨',
|
|
71
|
+
'["episodic", "semantic"]',
|
|
72
|
+
0.7,
|
|
73
|
+
1.1
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
-- Insert supported_by relation type (Structural category)
|
|
77
|
+
INSERT OR IGNORE INTO relation_type_registry (type_name, category, description, applicable_types, default_confidence, search_boost)
|
|
78
|
+
VALUES (
|
|
79
|
+
'supported_by',
|
|
80
|
+
'Structural',
|
|
81
|
+
'근거 관계: Semantic Memory가 Episodic Memory에 의해 근거를 가짐',
|
|
82
|
+
'["semantic", "episodic"]',
|
|
83
|
+
0.7,
|
|
84
|
+
1.0
|
|
85
|
+
);
|
|
86
|
+
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triple 추출 전용 로거
|
|
3
|
+
*
|
|
4
|
+
* rawLLMOutput을 로그 파일에 저장합니다.
|
|
5
|
+
*
|
|
6
|
+
* 보안 정책:
|
|
7
|
+
* - PII/비밀 정보 마스킹 적용
|
|
8
|
+
* - logs/triple-extraction/ 디렉토리에 저장
|
|
9
|
+
* - 날짜별 파일로 저장 (예: 2025-01-15.log)
|
|
10
|
+
* - 30일 로테이션 (2.16에서 구현)
|
|
11
|
+
* - 성공 케이스 10% 샘플링, 실패 케이스 100% 저장 (2.15에서 구현)
|
|
12
|
+
*/
|
|
13
|
+
import type { TripleExtractionResult } from '../../shared/types/triple-extraction.js';
|
|
14
|
+
/**
|
|
15
|
+
* Triple 추출 로그 엔트리
|
|
16
|
+
*/
|
|
17
|
+
export interface TripleExtractionLogEntry {
|
|
18
|
+
timestamp: string;
|
|
19
|
+
memory_id?: string;
|
|
20
|
+
extraction_result: 'success' | 'failed';
|
|
21
|
+
triples_count: number;
|
|
22
|
+
failure_reason?: string;
|
|
23
|
+
steps?: {
|
|
24
|
+
canonicalization: boolean;
|
|
25
|
+
entityLinking: boolean;
|
|
26
|
+
};
|
|
27
|
+
raw_llm_output?: string;
|
|
28
|
+
observation_preview?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Triple 추출 로거 설정
|
|
32
|
+
*/
|
|
33
|
+
export interface TripleExtractionLoggerConfig {
|
|
34
|
+
/**
|
|
35
|
+
* 로그 디렉토리 (기본: process.cwd()/logs/triple-extraction)
|
|
36
|
+
*/
|
|
37
|
+
logDir?: string;
|
|
38
|
+
/**
|
|
39
|
+
* 로깅 활성화 여부 (기본: true)
|
|
40
|
+
*/
|
|
41
|
+
enabled?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* PII 마스킹 활성화 여부 (기본: true)
|
|
44
|
+
*/
|
|
45
|
+
enablePIIMasking?: boolean;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Triple 추출 전용 로거
|
|
49
|
+
*/
|
|
50
|
+
export declare class TripleExtractionLogger {
|
|
51
|
+
private config;
|
|
52
|
+
private logDir;
|
|
53
|
+
constructor(config?: TripleExtractionLoggerConfig);
|
|
54
|
+
/**
|
|
55
|
+
* Triple 추출 결과 로깅
|
|
56
|
+
*
|
|
57
|
+
* @param result Triple 추출 결과
|
|
58
|
+
* @param memoryId Episodic Memory ID (선택사항)
|
|
59
|
+
* @param observation Observation 텍스트 (선택사항, 미리보기용)
|
|
60
|
+
*/
|
|
61
|
+
logExtraction(result: TripleExtractionResult, memoryId?: string, observation?: string): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* 로그 엔트리 생성
|
|
64
|
+
*/
|
|
65
|
+
private createLogEntry;
|
|
66
|
+
/**
|
|
67
|
+
* 로그 디렉토리 생성
|
|
68
|
+
*/
|
|
69
|
+
private ensureLogDirectory;
|
|
70
|
+
/**
|
|
71
|
+
* 날짜별 로그 파일 경로 반환
|
|
72
|
+
* 형식: YYYY-MM-DD.log
|
|
73
|
+
*/
|
|
74
|
+
private getLogFilePath;
|
|
75
|
+
/**
|
|
76
|
+
* 로그 파일 목록 조회
|
|
77
|
+
*
|
|
78
|
+
* @returns 로그 파일 경로 배열
|
|
79
|
+
*/
|
|
80
|
+
listLogFiles(): Promise<string[]>;
|
|
81
|
+
/**
|
|
82
|
+
* 오래된 로그 파일 삭제 (30일 이상)
|
|
83
|
+
*
|
|
84
|
+
* @param retentionDays 보존 기간 (일) (기본: 30일)
|
|
85
|
+
*/
|
|
86
|
+
deleteOldLogs(retentionDays?: number): Promise<number>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* 싱글톤 인스턴스
|
|
90
|
+
*/
|
|
91
|
+
export declare const tripleExtractionLogger: TripleExtractionLogger;
|
|
92
|
+
//# sourceMappingURL=triple-extraction-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triple-extraction-logger.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/logging/triple-extraction-logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAEtF;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,SAAS,GAAG,QAAQ,CAAC;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE;QACN,gBAAgB,EAAE,OAAO,CAAC;QAC1B,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;IACF,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC3C;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAyC;IACvD,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,GAAE,4BAAiC;IASrD;;;;;;OAMG;IACG,aAAa,CACjB,MAAM,EAAE,sBAAsB,EAC9B,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC;IA4BhB;;OAEG;IACH,OAAO,CAAC,cAAc;IA4DtB;;OAEG;YACW,kBAAkB;IAShC;;;OAGG;IACH,OAAO,CAAC,cAAc;IAOtB;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAiBvC;;;;OAIG;IACG,aAAa,CAAC,aAAa,GAAE,MAAW,GAAG,OAAO,CAAC,MAAM,CAAC;CA+BjE;AAED;;GAEG;AACH,eAAO,MAAM,sBAAsB,wBAA+B,CAAC"}
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triple 추출 전용 로거
|
|
3
|
+
*
|
|
4
|
+
* rawLLMOutput을 로그 파일에 저장합니다.
|
|
5
|
+
*
|
|
6
|
+
* 보안 정책:
|
|
7
|
+
* - PII/비밀 정보 마스킹 적용
|
|
8
|
+
* - logs/triple-extraction/ 디렉토리에 저장
|
|
9
|
+
* - 날짜별 파일로 저장 (예: 2025-01-15.log)
|
|
10
|
+
* - 30일 로테이션 (2.16에서 구현)
|
|
11
|
+
* - 성공 케이스 10% 샘플링, 실패 케이스 100% 저장 (2.15에서 구현)
|
|
12
|
+
*/
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import fsPromises from 'fs/promises';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import { PIIMasker } from '../../shared/utils/pii-masker.js';
|
|
17
|
+
/**
|
|
18
|
+
* Triple 추출 전용 로거
|
|
19
|
+
*/
|
|
20
|
+
export class TripleExtractionLogger {
|
|
21
|
+
config;
|
|
22
|
+
logDir;
|
|
23
|
+
constructor(config = {}) {
|
|
24
|
+
this.config = {
|
|
25
|
+
logDir: config.logDir ?? path.join(process.cwd(), 'logs', 'triple-extraction'),
|
|
26
|
+
enabled: config.enabled ?? true,
|
|
27
|
+
enablePIIMasking: config.enablePIIMasking !== false
|
|
28
|
+
};
|
|
29
|
+
this.logDir = this.config.logDir;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Triple 추출 결과 로깅
|
|
33
|
+
*
|
|
34
|
+
* @param result Triple 추출 결과
|
|
35
|
+
* @param memoryId Episodic Memory ID (선택사항)
|
|
36
|
+
* @param observation Observation 텍스트 (선택사항, 미리보기용)
|
|
37
|
+
*/
|
|
38
|
+
async logExtraction(result, memoryId, observation) {
|
|
39
|
+
if (!this.config.enabled) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
// 로그 디렉토리 생성
|
|
44
|
+
await this.ensureLogDirectory();
|
|
45
|
+
// 로그 엔트리 생성
|
|
46
|
+
const entry = this.createLogEntry(result, memoryId, observation);
|
|
47
|
+
// 날짜별 로그 파일 경로
|
|
48
|
+
const logFilePath = this.getLogFilePath();
|
|
49
|
+
// 로그 엔트리 포맷팅 (JSON Lines 형식)
|
|
50
|
+
const logLine = JSON.stringify(entry) + '\n';
|
|
51
|
+
// 파일에 추가 (비동기)
|
|
52
|
+
await fsPromises.appendFile(logFilePath, logLine);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
// 파일 로깅 실패는 무시 (콘솔 로거 사용)
|
|
56
|
+
console.error('TripleExtractionLogger: 로그 파일 쓰기 실패', {
|
|
57
|
+
error: error instanceof Error ? error.message : String(error)
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* 로그 엔트리 생성
|
|
63
|
+
*/
|
|
64
|
+
createLogEntry(result, memoryId, observation) {
|
|
65
|
+
const extractionResult = result.triples.length > 0 ? 'success' : 'failed';
|
|
66
|
+
// rawLLMOutput 마스킹
|
|
67
|
+
let maskedRawOutput;
|
|
68
|
+
if (result.extractionInfo.rawLLMOutput) {
|
|
69
|
+
if (this.config.enablePIIMasking) {
|
|
70
|
+
const maskingResult = PIIMasker.mask(result.extractionInfo.rawLLMOutput);
|
|
71
|
+
maskedRawOutput = maskingResult.masked;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
maskedRawOutput = result.extractionInfo.rawLLMOutput;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// observation 미리보기 마스킹 (최대 200자)
|
|
78
|
+
let observationPreview;
|
|
79
|
+
if (observation) {
|
|
80
|
+
const preview = observation.length > 200
|
|
81
|
+
? observation.substring(0, 200) + '...'
|
|
82
|
+
: observation;
|
|
83
|
+
if (this.config.enablePIIMasking) {
|
|
84
|
+
const maskingResult = PIIMasker.mask(preview);
|
|
85
|
+
observationPreview = maskingResult.masked;
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
observationPreview = preview;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const entry = {
|
|
92
|
+
timestamp: new Date().toISOString(),
|
|
93
|
+
memory_id: memoryId,
|
|
94
|
+
extraction_result: extractionResult,
|
|
95
|
+
triples_count: result.triples.length,
|
|
96
|
+
steps: result.extractionInfo.steps
|
|
97
|
+
};
|
|
98
|
+
// 실패 시 failure_reason 추가
|
|
99
|
+
if (result.extractionInfo.failureReason) {
|
|
100
|
+
entry.failure_reason = result.extractionInfo.failureReason;
|
|
101
|
+
}
|
|
102
|
+
// rawLLMOutput 추가 (마스킹된)
|
|
103
|
+
if (maskedRawOutput) {
|
|
104
|
+
entry.raw_llm_output = maskedRawOutput;
|
|
105
|
+
}
|
|
106
|
+
// observation 미리보기 추가 (마스킹된)
|
|
107
|
+
if (observationPreview) {
|
|
108
|
+
entry.observation_preview = observationPreview;
|
|
109
|
+
}
|
|
110
|
+
return entry;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 로그 디렉토리 생성
|
|
114
|
+
*/
|
|
115
|
+
async ensureLogDirectory() {
|
|
116
|
+
try {
|
|
117
|
+
await fsPromises.access(this.logDir);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
// 디렉토리가 없으면 생성
|
|
121
|
+
await fsPromises.mkdir(this.logDir, { recursive: true });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* 날짜별 로그 파일 경로 반환
|
|
126
|
+
* 형식: YYYY-MM-DD.log
|
|
127
|
+
*/
|
|
128
|
+
getLogFilePath() {
|
|
129
|
+
const today = new Date();
|
|
130
|
+
const dateStr = today.toISOString().split('T')[0]; // YYYY-MM-DD
|
|
131
|
+
const fileName = `${dateStr}.log`;
|
|
132
|
+
return path.join(this.logDir, fileName);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* 로그 파일 목록 조회
|
|
136
|
+
*
|
|
137
|
+
* @returns 로그 파일 경로 배열
|
|
138
|
+
*/
|
|
139
|
+
async listLogFiles() {
|
|
140
|
+
try {
|
|
141
|
+
await this.ensureLogDirectory();
|
|
142
|
+
const files = await fsPromises.readdir(this.logDir);
|
|
143
|
+
return files
|
|
144
|
+
.filter(file => file.endsWith('.log'))
|
|
145
|
+
.map(file => path.join(this.logDir, file))
|
|
146
|
+
.sort()
|
|
147
|
+
.reverse(); // 최신 파일 먼저
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.error('TripleExtractionLogger: 로그 파일 목록 조회 실패', {
|
|
151
|
+
error: error instanceof Error ? error.message : String(error)
|
|
152
|
+
});
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* 오래된 로그 파일 삭제 (30일 이상)
|
|
158
|
+
*
|
|
159
|
+
* @param retentionDays 보존 기간 (일) (기본: 30일)
|
|
160
|
+
*/
|
|
161
|
+
async deleteOldLogs(retentionDays = 30) {
|
|
162
|
+
try {
|
|
163
|
+
await this.ensureLogDirectory();
|
|
164
|
+
const files = await fsPromises.readdir(this.logDir);
|
|
165
|
+
const now = Date.now();
|
|
166
|
+
const retentionMs = retentionDays * 24 * 60 * 60 * 1000;
|
|
167
|
+
let deletedCount = 0;
|
|
168
|
+
for (const file of files) {
|
|
169
|
+
if (!file.endsWith('.log')) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const filePath = path.join(this.logDir, file);
|
|
173
|
+
const stats = await fsPromises.stat(filePath);
|
|
174
|
+
const fileAge = now - stats.mtimeMs;
|
|
175
|
+
if (fileAge > retentionMs) {
|
|
176
|
+
await fsPromises.unlink(filePath);
|
|
177
|
+
deletedCount++;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return deletedCount;
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
console.error('TripleExtractionLogger: 오래된 로그 파일 삭제 실패', {
|
|
184
|
+
error: error instanceof Error ? error.message : String(error)
|
|
185
|
+
});
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 싱글톤 인스턴스
|
|
192
|
+
*/
|
|
193
|
+
export const tripleExtractionLogger = new TripleExtractionLogger();
|
|
194
|
+
//# sourceMappingURL=triple-extraction-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"triple-extraction-logger.js","sourceRoot":"","sources":["../../../src/infrastructure/logging/triple-extraction-logger.ts"],"names":[],"mappings":"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"}
|