aiwf 0.3.21 → 0.3.23

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 (140) hide show
  1. package/package.json +3 -2
  2. package/src/cli/index.js +39 -336
  3. package/src/lib/installer.js +310 -90
  4. package/src/utils/messages.js +24 -0
  5. package/src/DEPENDENCY_MAP.md +0 -90
  6. package/src/cli/cache-cli.js +0 -459
  7. package/src/cli/checkpoint-cli.js +0 -417
  8. package/src/cli/language-cli.js +0 -286
  9. package/src/cli/sprint-cli.js +0 -287
  10. package/src/commands/ai-tool.js +0 -383
  11. package/src/commands/compress.js +0 -60
  12. package/src/commands/create-project.js +0 -160
  13. package/src/commands/evaluate.js +0 -459
  14. package/src/commands/persona.js +0 -309
  15. package/src/commands/sprint-task.js +0 -262
  16. package/src/commands/token.js +0 -312
  17. package/src/lib/ai-persona-manager.js +0 -711
  18. package/src/lib/cache-system.js +0 -332
  19. package/src/lib/context-engine.js +0 -568
  20. package/src/lib/github-integration.js +0 -402
  21. package/src/lib/memory-profiler.js +0 -471
  22. package/src/lib/metrics-collector.js +0 -885
  23. package/src/lib/offline-detector.js +0 -386
  24. package/src/lib/resource-loader-enhanced.js +0 -399
  25. package/src/lib/resource-loader.js +0 -250
  26. package/src/lib/resources/commands/ai-persona.js +0 -602
  27. package/src/lib/resources/commands/compress-context.js +0 -389
  28. package/src/lib/resources/commands/evaluate.js +0 -246
  29. package/src/lib/resources/commands/persona-context-apply.js +0 -562
  30. package/src/lib/resources/commands/token-tracking.js +0 -443
  31. package/src/lib/resources/config/commit-patterns.js +0 -103
  32. package/src/lib/resources/config/language.json +0 -6
  33. package/src/lib/resources/personas/PERSONA_INDEX.md +0 -57
  34. package/src/lib/resources/personas/analyst.json +0 -44
  35. package/src/lib/resources/personas/architect/best_practices.md +0 -136
  36. package/src/lib/resources/personas/architect/knowledge_base.md +0 -377
  37. package/src/lib/resources/personas/architect.json +0 -44
  38. package/src/lib/resources/personas/backend/best_practices.md +0 -766
  39. package/src/lib/resources/personas/backend/knowledge_base.md +0 -1070
  40. package/src/lib/resources/personas/data_analyst/best_practices.md +0 -594
  41. package/src/lib/resources/personas/data_analyst/knowledge_base.md +0 -1057
  42. package/src/lib/resources/personas/developer.json +0 -44
  43. package/src/lib/resources/personas/developer.md +0 -37
  44. package/src/lib/resources/personas/evaluation_criteria.json +0 -137
  45. package/src/lib/resources/personas/frontend/best_practices.md +0 -445
  46. package/src/lib/resources/personas/frontend/knowledge_base.md +0 -729
  47. package/src/lib/resources/personas/persona-index.json +0 -31
  48. package/src/lib/resources/personas/reviewer.json +0 -44
  49. package/src/lib/resources/personas/security/best_practices.md +0 -307
  50. package/src/lib/resources/personas/security/knowledge_base.md +0 -498
  51. package/src/lib/resources/personas/tester.json +0 -44
  52. package/src/lib/resources/templates/README.md +0 -78
  53. package/src/lib/resources/templates/api-server/config.json +0 -64
  54. package/src/lib/resources/templates/api-server/template/.aiwf/config.json +0 -56
  55. package/src/lib/resources/templates/api-server/template/.aiwf/feature-ledger.json +0 -42
  56. package/src/lib/resources/templates/api-server/template/.aiwf/personas/backend-engineer.json +0 -40
  57. package/src/lib/resources/templates/api-server/template/.aiwf/scripts/cli.js +0 -111
  58. package/src/lib/resources/templates/api-server/template/.env.example +0 -29
  59. package/src/lib/resources/templates/api-server/template/.eslintrc.json +0 -23
  60. package/src/lib/resources/templates/api-server/template/README.md +0 -171
  61. package/src/lib/resources/templates/api-server/template/jest.config.js +0 -26
  62. package/src/lib/resources/templates/api-server/template/nodemon.json +0 -9
  63. package/src/lib/resources/templates/api-server/template/package.json +0 -61
  64. package/src/lib/resources/templates/api-server/template/src/app.ts +0 -60
  65. package/src/lib/resources/templates/api-server/template/src/config/swagger.ts +0 -38
  66. package/src/lib/resources/templates/api-server/template/src/controllers/aiwfController.ts +0 -54
  67. package/src/lib/resources/templates/api-server/template/src/controllers/authController.ts +0 -128
  68. package/src/lib/resources/templates/api-server/template/src/controllers/statusController.ts +0 -28
  69. package/src/lib/resources/templates/api-server/template/src/index.ts +0 -28
  70. package/src/lib/resources/templates/api-server/template/src/middleware/aiwfMiddleware.ts +0 -50
  71. package/src/lib/resources/templates/api-server/template/src/middleware/authMiddleware.ts +0 -41
  72. package/src/lib/resources/templates/api-server/template/src/middleware/errorHandler.ts +0 -42
  73. package/src/lib/resources/templates/api-server/template/src/middleware/notFoundHandler.ts +0 -14
  74. package/src/lib/resources/templates/api-server/template/src/middleware/requestLogger.ts +0 -20
  75. package/src/lib/resources/templates/api-server/template/src/routes/aiwf.ts +0 -31
  76. package/src/lib/resources/templates/api-server/template/src/routes/index.ts +0 -13
  77. package/src/lib/resources/templates/api-server/template/src/routes/v1/index.ts +0 -67
  78. package/src/lib/resources/templates/api-server/template/src/utils/logger.ts +0 -37
  79. package/src/lib/resources/templates/api-server/template/tests/app.test.ts +0 -40
  80. package/src/lib/resources/templates/api-server/template/tsconfig.json +0 -43
  81. package/src/lib/resources/templates/npm-library/config.json +0 -61
  82. package/src/lib/resources/templates/npm-library/template/LICENSE +0 -21
  83. package/src/lib/resources/templates/npm-library/template/README.md +0 -201
  84. package/src/lib/resources/templates/npm-library/template/package.json +0 -78
  85. package/src/lib/resources/templates/web-app/config.json +0 -53
  86. package/src/lib/resources/templates/web-app/template/.aiwf/config.json +0 -54
  87. package/src/lib/resources/templates/web-app/template/.aiwf/feature-ledger.json +0 -37
  88. package/src/lib/resources/templates/web-app/template/.aiwf/personas/fullstack-developer.json +0 -40
  89. package/src/lib/resources/templates/web-app/template/.aiwf/scripts/cli.js +0 -110
  90. package/src/lib/resources/templates/web-app/template/.eslintrc.cjs +0 -20
  91. package/src/lib/resources/templates/web-app/template/README.md +0 -151
  92. package/src/lib/resources/templates/web-app/template/index.html +0 -14
  93. package/src/lib/resources/templates/web-app/template/package.json +0 -44
  94. package/src/lib/resources/templates/web-app/template/postcss.config.js +0 -6
  95. package/src/lib/resources/templates/web-app/template/public/vite.svg +0 -1
  96. package/src/lib/resources/templates/web-app/template/src/App.tsx +0 -21
  97. package/src/lib/resources/templates/web-app/template/src/components/Layout.tsx +0 -57
  98. package/src/lib/resources/templates/web-app/template/src/components/aiwf/ContextStatus.tsx +0 -107
  99. package/src/lib/resources/templates/web-app/template/src/components/aiwf/TokenUsage.tsx +0 -71
  100. package/src/lib/resources/templates/web-app/template/src/index.css +0 -60
  101. package/src/lib/resources/templates/web-app/template/src/main.tsx +0 -10
  102. package/src/lib/resources/templates/web-app/template/src/pages/AiwfDashboard.tsx +0 -57
  103. package/src/lib/resources/templates/web-app/template/src/pages/HomePage.tsx +0 -89
  104. package/src/lib/resources/templates/web-app/template/src/pages/NotFound.tsx +0 -25
  105. package/src/lib/resources/templates/web-app/template/src/stores/aiwfStore.ts +0 -126
  106. package/src/lib/resources/templates/web-app/template/src/types/global.d.ts +0 -9
  107. package/src/lib/resources/templates/web-app/template/src/vite-env.d.ts +0 -1
  108. package/src/lib/resources/templates/web-app/template/tailwind.config.js +0 -30
  109. package/src/lib/resources/templates/web-app/template/tsconfig.json +0 -37
  110. package/src/lib/resources/templates/web-app/template/tsconfig.node.json +0 -10
  111. package/src/lib/resources/templates/web-app/template/vite.config.ts +0 -23
  112. package/src/lib/resources/utils/background-monitor.js +0 -223
  113. package/src/lib/resources/utils/compression-metrics.js +0 -1031
  114. package/src/lib/resources/utils/compression-strategies.js +0 -557
  115. package/src/lib/resources/utils/content-normalizer.js +0 -455
  116. package/src/lib/resources/utils/context-compressor.js +0 -583
  117. package/src/lib/resources/utils/context-rule-parser.js +0 -180
  118. package/src/lib/resources/utils/context-token-monitor.js +0 -463
  119. package/src/lib/resources/utils/context-update-manager.js +0 -502
  120. package/src/lib/resources/utils/git-utils.js +0 -220
  121. package/src/lib/resources/utils/importance-classifier.js +0 -645
  122. package/src/lib/resources/utils/information-filter.js +0 -710
  123. package/src/lib/resources/utils/persona-aware-compressor.js +0 -598
  124. package/src/lib/resources/utils/prompt-injector.js +0 -242
  125. package/src/lib/resources/utils/simplified-evaluator.js +0 -119
  126. package/src/lib/resources/utils/text-summarizer.js +0 -492
  127. package/src/lib/resources/utils/token-counter.js +0 -177
  128. package/src/lib/resources/utils/token-monitor.js +0 -484
  129. package/src/lib/resources/utils/token-reporter.js +0 -452
  130. package/src/lib/resources/utils/token-storage.js +0 -433
  131. package/src/lib/resources/utils/token-tracker.js +0 -283
  132. package/src/lib/state/priority-calculator.js +0 -217
  133. package/src/lib/state/state-index.js +0 -155
  134. package/src/lib/state/task-scanner.js +0 -328
  135. package/src/lib/task-analyzer.js +0 -599
  136. package/src/lib/template-cache-system.js +0 -559
  137. package/src/lib/template-downloader.js +0 -467
  138. package/src/lib/template-version-manager.js +0 -491
  139. package/src/lib/token-optimizer.js +0 -517
  140. package/src/utils/engineering-guard.js +0 -399
@@ -1,710 +0,0 @@
1
- import { ImportanceClassifier } from './importance-classifier.js';
2
- import { TokenCounter } from './token-counter.js';
3
-
4
- /**
5
- * 선택적 정보 필터링 시스템
6
- */
7
- export class InformationFilter {
8
- constructor() {
9
- this.classifier = new ImportanceClassifier();
10
- this.tokenCounter = new TokenCounter();
11
- this.initializeFilters();
12
- }
13
-
14
- /**
15
- * 필터를 초기화합니다
16
- */
17
- initializeFilters() {
18
- // 필터 타입 정의
19
- this.filterTypes = {
20
- importance: {
21
- name: 'Importance Filter',
22
- description: '중요도 기반 필터링',
23
- levels: ['critical', 'high', 'medium', 'low']
24
- },
25
- content: {
26
- name: 'Content Filter',
27
- description: '콘텐츠 타입 기반 필터링',
28
- types: ['headers', 'lists', 'text', 'code', 'links', 'images']
29
- },
30
- temporal: {
31
- name: 'Temporal Filter',
32
- description: '시간 기반 필터링',
33
- periods: ['recent', 'current', 'old', 'archived']
34
- },
35
- structural: {
36
- name: 'Structural Filter',
37
- description: '구조 기반 필터링',
38
- elements: ['sections', 'subsections', 'paragraphs', 'sentences']
39
- },
40
- keyword: {
41
- name: 'Keyword Filter',
42
- description: '키워드 기반 필터링',
43
- modes: ['include', 'exclude', 'highlight']
44
- }
45
- };
46
-
47
- // 기본 필터 규칙
48
- this.defaultRules = {
49
- preserve: {
50
- importance: ['critical', 'high'],
51
- content: ['headers', 'lists'],
52
- keywords: ['goal', 'requirement', 'task', 'milestone', '목표', '요구사항', '태스크'],
53
- patterns: [/^\s*[-*+]\s+\[.\]/] // 체크박스 항목
54
- },
55
- compress: {
56
- importance: ['medium'],
57
- content: ['text'],
58
- maxLength: 200
59
- },
60
- remove: {
61
- importance: ['low'],
62
- content: ['old_logs', 'examples'],
63
- keywords: ['deprecated', 'obsolete', 'old', '구식', '폐기'],
64
- patterns: [/\[completed\]/i, /\[archived\]/i]
65
- }
66
- };
67
-
68
- // 필터 통계
69
- this.stats = {
70
- processed: 0,
71
- preserved: 0,
72
- compressed: 0,
73
- removed: 0,
74
- tokensOriginal: 0,
75
- tokensFiltered: 0
76
- };
77
- }
78
-
79
- /**
80
- * 콘텐츠를 필터링합니다
81
- * @param {string} content - 필터링할 콘텐츠
82
- * @param {Object} options - 필터링 옵션
83
- * @returns {Object} 필터링 결과
84
- */
85
- filter(content, options = {}) {
86
- const startTime = Date.now();
87
-
88
- const {
89
- minImportance = 'medium',
90
- maxTokens = null,
91
- preserveStructure = true,
92
- customRules = {},
93
- includeMetadata = true
94
- } = options;
95
-
96
- // 통계 초기화
97
- this.stats.tokensOriginal = this.tokenCounter.countTokens(content);
98
- this.stats.processed = 0;
99
- this.stats.preserved = 0;
100
- this.stats.compressed = 0;
101
- this.stats.removed = 0;
102
-
103
- // 규칙 병합
104
- const rules = this.mergeRules(this.defaultRules, customRules);
105
-
106
- // 1. 중요도 분석
107
- const importanceResult = this.classifier.analyzeImportance(content);
108
- const sections = importanceResult.sections;
109
-
110
- // 2. 섹션별 필터링
111
- const filteredSections = this.filterSections(sections, minImportance, rules);
112
-
113
- // 3. 토큰 제한 적용
114
- const tokenLimitedSections = maxTokens
115
- ? this.applyTokenLimit(filteredSections, maxTokens)
116
- : filteredSections;
117
-
118
- // 4. 구조 재구성
119
- const filteredContent = preserveStructure
120
- ? this.reconstructStructure(tokenLimitedSections)
121
- : this.reconstructFlat(tokenLimitedSections);
122
-
123
- // 5. 후처리
124
- const finalContent = this.postProcess(filteredContent, rules);
125
-
126
- // 통계 업데이트
127
- this.stats.tokensFiltered = this.tokenCounter.countTokens(finalContent);
128
-
129
- const result = {
130
- original: content,
131
- filtered: finalContent,
132
- compressionRatio: this.calculateCompressionRatio(content, finalContent),
133
- sectionsProcessed: sections.length,
134
- sectionsPreserved: this.stats.preserved,
135
- sectionsCompressed: this.stats.compressed,
136
- sectionsRemoved: this.stats.removed,
137
- processingTime: Date.now() - startTime
138
- };
139
-
140
- if (includeMetadata) {
141
- result.metadata = this.generateFilterMetadata(sections, filteredSections, rules);
142
- }
143
-
144
- return result;
145
- }
146
-
147
- /**
148
- * 규칙을 병합합니다
149
- * @param {Object} defaultRules - 기본 규칙
150
- * @param {Object} customRules - 사용자 규칙
151
- * @returns {Object} 병합된 규칙
152
- */
153
- mergeRules(defaultRules, customRules) {
154
- const merged = JSON.parse(JSON.stringify(defaultRules));
155
-
156
- // 사용자 규칙 적용
157
- for (const [category, rules] of Object.entries(customRules)) {
158
- if (merged[category]) {
159
- merged[category] = { ...merged[category], ...rules };
160
- } else {
161
- merged[category] = rules;
162
- }
163
- }
164
-
165
- return merged;
166
- }
167
-
168
- /**
169
- * 섹션들을 필터링합니다
170
- * @param {Array<Object>} sections - 섹션 배열
171
- * @param {string} minImportance - 최소 중요도
172
- * @param {Object} rules - 필터링 규칙
173
- * @returns {Array<Object>} 필터링된 섹션 배열
174
- */
175
- filterSections(sections, minImportance, rules) {
176
- const filtered = [];
177
- const importanceOrder = ['critical', 'high', 'medium', 'low'];
178
- const minIndex = importanceOrder.indexOf(minImportance);
179
-
180
- for (const section of sections) {
181
- this.stats.processed++;
182
-
183
- const decision = this.makeFilterDecision(section, minImportance, rules);
184
-
185
- switch (decision.action) {
186
- case 'preserve':
187
- filtered.push({
188
- ...section,
189
- filterAction: 'preserve',
190
- filterReason: decision.reason
191
- });
192
- this.stats.preserved++;
193
- break;
194
-
195
- case 'compress':
196
- const compressed = this.compressSection(section, rules.compress);
197
- filtered.push({
198
- ...compressed,
199
- filterAction: 'compress',
200
- filterReason: decision.reason
201
- });
202
- this.stats.compressed++;
203
- break;
204
-
205
- case 'remove':
206
- // 섹션 제거 (필터링된 배열에 추가하지 않음)
207
- this.stats.removed++;
208
- break;
209
-
210
- default:
211
- // 기본적으로 보존
212
- filtered.push(section);
213
- this.stats.preserved++;
214
- }
215
- }
216
-
217
- return filtered;
218
- }
219
-
220
- /**
221
- * 섹션에 대한 필터링 결정을 내립니다
222
- * @param {Object} section - 섹션 객체
223
- * @param {string} minImportance - 최소 중요도
224
- * @param {Object} rules - 필터링 규칙
225
- * @returns {Object} 필터링 결정
226
- */
227
- makeFilterDecision(section, minImportance, rules) {
228
- const importance = section.importance;
229
- const content = section.content;
230
- const name = section.name;
231
-
232
- // 1. 보존 규칙 확인
233
- if (this.matchesPreserveRules(section, rules.preserve)) {
234
- return { action: 'preserve', reason: 'matches_preserve_rules' };
235
- }
236
-
237
- // 2. 제거 규칙 확인
238
- if (this.matchesRemoveRules(section, rules.remove)) {
239
- return { action: 'remove', reason: 'matches_remove_rules' };
240
- }
241
-
242
- // 3. 중요도 기반 결정
243
- const importanceOrder = ['critical', 'high', 'medium', 'low'];
244
- const sectionIndex = importanceOrder.indexOf(importance);
245
- const minIndex = importanceOrder.indexOf(minImportance);
246
-
247
- if (sectionIndex <= minIndex) {
248
- // 4. 압축 규칙 확인
249
- if (this.matchesCompressRules(section, rules.compress)) {
250
- return { action: 'compress', reason: 'matches_compress_rules' };
251
- }
252
- return { action: 'preserve', reason: 'meets_importance_threshold' };
253
- } else {
254
- return { action: 'remove', reason: 'below_importance_threshold' };
255
- }
256
- }
257
-
258
- /**
259
- * 보존 규칙과 매치되는지 확인합니다
260
- * @param {Object} section - 섹션 객체
261
- * @param {Object} preserveRules - 보존 규칙
262
- * @returns {boolean} 매치 여부
263
- */
264
- matchesPreserveRules(section, preserveRules) {
265
- // 중요도 확인
266
- if (preserveRules.importance && preserveRules.importance.includes(section.importance)) {
267
- return true;
268
- }
269
-
270
- // 키워드 확인
271
- if (preserveRules.keywords) {
272
- const content = section.content.toLowerCase();
273
- const name = section.name.toLowerCase();
274
-
275
- for (const keyword of preserveRules.keywords) {
276
- if (content.includes(keyword.toLowerCase()) || name.includes(keyword.toLowerCase())) {
277
- return true;
278
- }
279
- }
280
- }
281
-
282
- // 패턴 확인
283
- if (preserveRules.patterns) {
284
- for (const pattern of preserveRules.patterns) {
285
- if (pattern.test(section.content)) {
286
- return true;
287
- }
288
- }
289
- }
290
-
291
- // 콘텐츠 타입 확인
292
- if (preserveRules.content) {
293
- const contentType = this.identifyContentType(section.content);
294
- if (preserveRules.content.includes(contentType)) {
295
- return true;
296
- }
297
- }
298
-
299
- return false;
300
- }
301
-
302
- /**
303
- * 제거 규칙과 매치되는지 확인합니다
304
- * @param {Object} section - 섹션 객체
305
- * @param {Object} removeRules - 제거 규칙
306
- * @returns {boolean} 매치 여부
307
- */
308
- matchesRemoveRules(section, removeRules) {
309
- // 중요도 확인
310
- if (removeRules.importance && removeRules.importance.includes(section.importance)) {
311
- return true;
312
- }
313
-
314
- // 키워드 확인
315
- if (removeRules.keywords) {
316
- const content = section.content.toLowerCase();
317
- const name = section.name.toLowerCase();
318
-
319
- for (const keyword of removeRules.keywords) {
320
- if (content.includes(keyword.toLowerCase()) || name.includes(keyword.toLowerCase())) {
321
- return true;
322
- }
323
- }
324
- }
325
-
326
- // 패턴 확인
327
- if (removeRules.patterns) {
328
- for (const pattern of removeRules.patterns) {
329
- if (pattern.test(section.content)) {
330
- return true;
331
- }
332
- }
333
- }
334
-
335
- // 오래된 로그 확인
336
- if (this.isOldLog(section.content)) {
337
- return true;
338
- }
339
-
340
- return false;
341
- }
342
-
343
- /**
344
- * 압축 규칙과 매치되는지 확인합니다
345
- * @param {Object} section - 섹션 객체
346
- * @param {Object} compressRules - 압축 규칙
347
- * @returns {boolean} 매치 여부
348
- */
349
- matchesCompressRules(section, compressRules) {
350
- // 중요도 확인
351
- if (compressRules.importance && compressRules.importance.includes(section.importance)) {
352
- return true;
353
- }
354
-
355
- // 길이 확인
356
- if (compressRules.maxLength && section.content.length > compressRules.maxLength) {
357
- return true;
358
- }
359
-
360
- // 콘텐츠 타입 확인
361
- if (compressRules.content) {
362
- const contentType = this.identifyContentType(section.content);
363
- if (compressRules.content.includes(contentType)) {
364
- return true;
365
- }
366
- }
367
-
368
- return false;
369
- }
370
-
371
- /**
372
- * 콘텐츠 타입을 식별합니다
373
- * @param {string} content - 콘텐츠
374
- * @returns {string} 콘텐츠 타입
375
- */
376
- identifyContentType(content) {
377
- // 헤더
378
- if (content.match(/^#{1,6}\s+/)) {
379
- return 'headers';
380
- }
381
-
382
- // 목록
383
- if (content.match(/^\s*[-*+]\s+/) || content.match(/^\s*\d+\.\s+/)) {
384
- return 'lists';
385
- }
386
-
387
- // 코드 블록
388
- if (content.match(/```[\s\S]*?```/) || content.match(/`[^`]+`/)) {
389
- return 'code';
390
- }
391
-
392
- // 링크
393
- if (content.match(/\[([^\]]+)\]\(([^)]+)\)/)) {
394
- return 'links';
395
- }
396
-
397
- // 이미지
398
- if (content.match(/!\[([^\]]*)\]\(([^)]+)\)/)) {
399
- return 'images';
400
- }
401
-
402
- // 일반 텍스트
403
- return 'text';
404
- }
405
-
406
- /**
407
- * 오래된 로그인지 확인합니다
408
- * @param {string} content - 콘텐츠
409
- * @returns {boolean} 오래된 로그 여부
410
- */
411
- isOldLog(content) {
412
- const datePattern = /\[(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\]/;
413
- const match = content.match(datePattern);
414
-
415
- if (match) {
416
- const logDate = new Date(match[1]);
417
- const cutoffDate = new Date();
418
- cutoffDate.setDate(cutoffDate.getDate() - 7); // 7일 전
419
-
420
- return logDate < cutoffDate;
421
- }
422
-
423
- return false;
424
- }
425
-
426
- /**
427
- * 섹션을 압축합니다
428
- * @param {Object} section - 섹션 객체
429
- * @param {Object} compressRules - 압축 규칙
430
- * @returns {Object} 압축된 섹션
431
- */
432
- compressSection(section, compressRules) {
433
- let compressed = section.content;
434
-
435
- // 긴 문단 요약
436
- if (compressRules.maxLength && compressed.length > compressRules.maxLength) {
437
- compressed = this.summarizeLongText(compressed, compressRules.maxLength);
438
- }
439
-
440
- // 중복 제거
441
- compressed = this.removeDuplicateLines(compressed);
442
-
443
- // 불필요한 공백 제거
444
- compressed = this.normalizeWhitespace(compressed);
445
-
446
- return {
447
- ...section,
448
- content: compressed,
449
- originalLength: section.content.length,
450
- compressedLength: compressed.length,
451
- compressionRatio: ((section.content.length - compressed.length) / section.content.length) * 100
452
- };
453
- }
454
-
455
- /**
456
- * 긴 텍스트를 요약합니다
457
- * @param {string} text - 요약할 텍스트
458
- * @param {number} maxLength - 최대 길이
459
- * @returns {string} 요약된 텍스트
460
- */
461
- summarizeLongText(text, maxLength) {
462
- if (text.length <= maxLength) return text;
463
-
464
- const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
465
-
466
- if (sentences.length <= 2) {
467
- return text.substring(0, maxLength) + '...';
468
- }
469
-
470
- // 첫 번째와 마지막 문장 유지
471
- const firstSentence = sentences[0].trim();
472
- const lastSentence = sentences[sentences.length - 1].trim();
473
- const summary = `${firstSentence}. ... ${lastSentence}.`;
474
-
475
- return summary.length > maxLength
476
- ? text.substring(0, maxLength) + '...'
477
- : summary;
478
- }
479
-
480
- /**
481
- * 중복된 라인을 제거합니다
482
- * @param {string} content - 콘텐츠
483
- * @returns {string} 중복이 제거된 콘텐츠
484
- */
485
- removeDuplicateLines(content) {
486
- const lines = content.split('\n');
487
- const uniqueLines = [];
488
- const seenLines = new Set();
489
-
490
- for (const line of lines) {
491
- const normalizedLine = line.trim().toLowerCase();
492
-
493
- if (normalizedLine === '' || !seenLines.has(normalizedLine)) {
494
- seenLines.add(normalizedLine);
495
- uniqueLines.push(line);
496
- }
497
- }
498
-
499
- return uniqueLines.join('\n');
500
- }
501
-
502
- /**
503
- * 공백을 정규화합니다
504
- * @param {string} content - 콘텐츠
505
- * @returns {string} 정규화된 콘텐츠
506
- */
507
- normalizeWhitespace(content) {
508
- return content
509
- .replace(/[ \t]+$/gm, '') // 줄 끝 공백 제거
510
- .replace(/\n{3,}/g, '\n\n') // 연속된 빈 줄 제거
511
- .replace(/^\s+|\s+$/g, ''); // 시작/끝 공백 제거
512
- }
513
-
514
- /**
515
- * 토큰 제한을 적용합니다
516
- * @param {Array<Object>} sections - 섹션 배열
517
- * @param {number} maxTokens - 최대 토큰 수
518
- * @returns {Array<Object>} 토큰 제한이 적용된 섹션 배열
519
- */
520
- applyTokenLimit(sections, maxTokens) {
521
- const result = [];
522
- let currentTokens = 0;
523
-
524
- // 중요도 순으로 정렬
525
- const sortedSections = [...sections].sort((a, b) => {
526
- const importanceOrder = { critical: 0, high: 1, medium: 2, low: 3 };
527
- return importanceOrder[a.importance] - importanceOrder[b.importance];
528
- });
529
-
530
- for (const section of sortedSections) {
531
- const sectionTokens = this.tokenCounter.countTokens(section.content);
532
-
533
- if (currentTokens + sectionTokens <= maxTokens) {
534
- result.push(section);
535
- currentTokens += sectionTokens;
536
- } else {
537
- // 남은 토큰으로 섹션 압축 시도
538
- const remainingTokens = maxTokens - currentTokens;
539
- if (remainingTokens > 50) { // 최소 50토큰 필요
540
- const compressedSection = this.compressSectionToTokenLimit(section, remainingTokens);
541
- if (compressedSection) {
542
- result.push(compressedSection);
543
- currentTokens += this.tokenCounter.countTokens(compressedSection.content);
544
- }
545
- }
546
- break;
547
- }
548
- }
549
-
550
- return result;
551
- }
552
-
553
- /**
554
- * 섹션을 토큰 제한에 맞춰 압축합니다
555
- * @param {Object} section - 섹션 객체
556
- * @param {number} targetTokens - 목표 토큰 수
557
- * @returns {Object|null} 압축된 섹션 또는 null
558
- */
559
- compressSectionToTokenLimit(section, targetTokens) {
560
- const content = section.content;
561
- const currentTokens = this.tokenCounter.countTokens(content);
562
-
563
- if (currentTokens <= targetTokens) {
564
- return section;
565
- }
566
-
567
- // 비율 계산
568
- const ratio = targetTokens / currentTokens;
569
- const targetLength = Math.floor(content.length * ratio);
570
-
571
- // 텍스트 압축
572
- const compressed = this.summarizeLongText(content, targetLength);
573
- const compressedTokens = this.tokenCounter.countTokens(compressed);
574
-
575
- if (compressedTokens <= targetTokens) {
576
- return {
577
- ...section,
578
- content: compressed,
579
- compressedForTokenLimit: true,
580
- originalTokens: currentTokens,
581
- compressedTokens: compressedTokens
582
- };
583
- }
584
-
585
- return null;
586
- }
587
-
588
- /**
589
- * 구조를 보존하여 재구성합니다
590
- * @param {Array<Object>} sections - 섹션 배열
591
- * @returns {string} 재구성된 콘텐츠
592
- */
593
- reconstructStructure(sections) {
594
- const sortedSections = sections.sort((a, b) => (a.startLine || 0) - (b.startLine || 0));
595
- return sortedSections.map(section => section.content).join('\n\n');
596
- }
597
-
598
- /**
599
- * 플랫한 구조로 재구성합니다
600
- * @param {Array<Object>} sections - 섹션 배열
601
- * @returns {string} 재구성된 콘텐츠
602
- */
603
- reconstructFlat(sections) {
604
- const importanceOrder = { critical: 0, high: 1, medium: 2, low: 3 };
605
- const sortedSections = sections.sort((a, b) => {
606
- return importanceOrder[a.importance] - importanceOrder[b.importance];
607
- });
608
-
609
- return sortedSections.map(section => section.content).join('\n\n');
610
- }
611
-
612
- /**
613
- * 후처리를 수행합니다
614
- * @param {string} content - 콘텐츠
615
- * @param {Object} rules - 규칙
616
- * @returns {string} 후처리된 콘텐츠
617
- */
618
- postProcess(content, rules) {
619
- let processed = content;
620
-
621
- // 최종 정리
622
- processed = this.normalizeWhitespace(processed);
623
-
624
- // 빈 섹션 제거
625
- processed = processed.replace(/^#{1,6}\s+.*$\n\n(?=^#{1,6}\s+.*$)/gm, '');
626
-
627
- // 연속된 구분선 제거
628
- processed = processed.replace(/---+\n---+/g, '---');
629
-
630
- return processed;
631
- }
632
-
633
- /**
634
- * 압축률을 계산합니다
635
- * @param {string} original - 원본 콘텐츠
636
- * @param {string} filtered - 필터링된 콘텐츠
637
- * @returns {number} 압축률 (백분율)
638
- */
639
- calculateCompressionRatio(original, filtered) {
640
- const originalTokens = this.tokenCounter.countTokens(original);
641
- const filteredTokens = this.tokenCounter.countTokens(filtered);
642
-
643
- if (originalTokens === 0) return 0;
644
- return ((originalTokens - filteredTokens) / originalTokens) * 100;
645
- }
646
-
647
- /**
648
- * 필터 메타데이터를 생성합니다
649
- * @param {Array<Object>} originalSections - 원본 섹션 배열
650
- * @param {Array<Object>} filteredSections - 필터링된 섹션 배열
651
- * @param {Object} rules - 적용된 규칙
652
- * @returns {Object} 메타데이터
653
- */
654
- generateFilterMetadata(originalSections, filteredSections, rules) {
655
- return {
656
- filteringDate: new Date().toISOString(),
657
- appliedRules: rules,
658
- statistics: {
659
- ...this.stats,
660
- preservationRate: (this.stats.preserved / this.stats.processed) * 100,
661
- compressionRate: (this.stats.compressed / this.stats.processed) * 100,
662
- removalRate: (this.stats.removed / this.stats.processed) * 100,
663
- tokenSavings: this.stats.tokensOriginal - this.stats.tokensFiltered
664
- },
665
- sectionAnalysis: {
666
- originalCount: originalSections.length,
667
- filteredCount: filteredSections.length,
668
- preservedSections: filteredSections.filter(s => s.filterAction === 'preserve').length,
669
- compressedSections: filteredSections.filter(s => s.filterAction === 'compress').length,
670
- removedSections: originalSections.length - filteredSections.length
671
- }
672
- };
673
- }
674
-
675
- /**
676
- * 필터 통계를 반환합니다
677
- * @returns {Object} 필터 통계
678
- */
679
- getStatistics() {
680
- return { ...this.stats };
681
- }
682
-
683
- /**
684
- * 필터를 재설정합니다
685
- */
686
- reset() {
687
- this.stats = {
688
- processed: 0,
689
- preserved: 0,
690
- compressed: 0,
691
- removed: 0,
692
- tokensOriginal: 0,
693
- tokensFiltered: 0
694
- };
695
- }
696
-
697
- /**
698
- * 리소스를 정리합니다
699
- */
700
- cleanup() {
701
- if (this.classifier) {
702
- this.classifier.cleanup();
703
- }
704
- if (this.tokenCounter) {
705
- this.tokenCounter.cleanup();
706
- }
707
- }
708
- }
709
-
710
- export default InformationFilter;