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,1031 +0,0 @@
1
- import { TokenCounter } from './token-counter.js';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- /**
6
- * 압축률 측정 및 성능 분석 도구
7
- */
8
- export class CompressionMetrics {
9
- constructor() {
10
- this.tokenCounter = new TokenCounter();
11
- this.measurements = new Map();
12
- this.benchmarks = new Map();
13
- this.initializeMetrics();
14
- }
15
-
16
- /**
17
- * 메트릭 시스템을 초기화합니다
18
- */
19
- initializeMetrics() {
20
- // 메트릭 타입 정의
21
- this.metricTypes = {
22
- compression: {
23
- name: 'Compression Metrics',
24
- description: '압축률 관련 메트릭',
25
- measurements: [
26
- 'compression_ratio',
27
- 'token_reduction',
28
- 'size_reduction',
29
- 'information_preservation'
30
- ]
31
- },
32
- performance: {
33
- name: 'Performance Metrics',
34
- description: '성능 관련 메트릭',
35
- measurements: [
36
- 'compression_time',
37
- 'decompression_time',
38
- 'memory_usage',
39
- 'throughput'
40
- ]
41
- },
42
- quality: {
43
- name: 'Quality Metrics',
44
- description: '품질 관련 메트릭',
45
- measurements: [
46
- 'readability_score',
47
- 'structure_preservation',
48
- 'semantic_similarity',
49
- 'information_loss'
50
- ]
51
- },
52
- efficiency: {
53
- name: 'Efficiency Metrics',
54
- description: '효율성 관련 메트릭',
55
- measurements: [
56
- 'tokens_per_second',
57
- 'compression_efficiency',
58
- 'resource_utilization',
59
- 'cost_effectiveness'
60
- ]
61
- }
62
- };
63
-
64
- // 벤치마크 기준점
65
- this.benchmarkThresholds = {
66
- compression_ratio: {
67
- excellent: 70,
68
- good: 50,
69
- acceptable: 30,
70
- poor: 10
71
- },
72
- compression_time: {
73
- excellent: 1000, // 1초
74
- good: 3000, // 3초
75
- acceptable: 5000, // 5초
76
- poor: 10000 // 10초
77
- },
78
- information_preservation: {
79
- excellent: 90,
80
- good: 80,
81
- acceptable: 70,
82
- poor: 50
83
- },
84
- readability_score: {
85
- excellent: 90,
86
- good: 80,
87
- acceptable: 70,
88
- poor: 50
89
- }
90
- };
91
-
92
- // 측정 히스토리
93
- this.measurementHistory = [];
94
- }
95
-
96
- /**
97
- * 압축 결과를 측정합니다
98
- * @param {string} original - 원본 콘텐츠
99
- * @param {string} compressed - 압축된 콘텐츠
100
- * @param {Object} metadata - 압축 메타데이터
101
- * @returns {Object} 측정 결과
102
- */
103
- measureCompression(original, compressed, metadata = {}) {
104
- const measurementId = this.generateMeasurementId();
105
- const timestamp = Date.now();
106
-
107
- const measurement = {
108
- id: measurementId,
109
- timestamp,
110
- originalContent: original,
111
- compressedContent: compressed,
112
- metadata,
113
- metrics: {},
114
- analysis: {},
115
- benchmark: {}
116
- };
117
-
118
- // 1. 압축 메트릭 계산
119
- measurement.metrics.compression = this.calculateCompressionMetrics(original, compressed);
120
-
121
- // 2. 성능 메트릭 계산
122
- measurement.metrics.performance = this.calculatePerformanceMetrics(metadata);
123
-
124
- // 3. 품질 메트릭 계산
125
- measurement.metrics.quality = this.calculateQualityMetrics(original, compressed, metadata);
126
-
127
- // 4. 효율성 메트릭 계산
128
- measurement.metrics.efficiency = this.calculateEfficiencyMetrics(original, compressed, metadata);
129
-
130
- // 5. 벤치마크 분석
131
- measurement.benchmark = this.analyzeBenchmark(measurement.metrics);
132
-
133
- // 6. 종합 분석
134
- measurement.analysis = this.generateAnalysis(measurement.metrics, measurement.benchmark);
135
-
136
- // 측정 결과 저장
137
- this.measurements.set(measurementId, measurement);
138
- this.measurementHistory.push(measurement);
139
-
140
- return measurement;
141
- }
142
-
143
- /**
144
- * 압축 메트릭을 계산합니다
145
- * @param {string} original - 원본 콘텐츠
146
- * @param {string} compressed - 압축된 콘텐츠
147
- * @returns {Object} 압축 메트릭
148
- */
149
- calculateCompressionMetrics(original, compressed) {
150
- const originalTokens = this.tokenCounter.countTokens(original);
151
- const compressedTokens = this.tokenCounter.countTokens(compressed);
152
- const originalSize = Buffer.byteLength(original, 'utf8');
153
- const compressedSize = Buffer.byteLength(compressed, 'utf8');
154
-
155
- const tokenReduction = originalTokens - compressedTokens;
156
- const sizeReduction = originalSize - compressedSize;
157
- const compressionRatio = originalTokens > 0 ? (tokenReduction / originalTokens) * 100 : 0;
158
- const sizeCompressionRatio = originalSize > 0 ? (sizeReduction / originalSize) * 100 : 0;
159
-
160
- // 정보 보존률 계산
161
- const informationPreservation = this.calculateInformationPreservation(original, compressed);
162
-
163
- return {
164
- original_tokens: originalTokens,
165
- compressed_tokens: compressedTokens,
166
- token_reduction: tokenReduction,
167
- token_reduction_percentage: compressionRatio,
168
- original_size: originalSize,
169
- compressed_size: compressedSize,
170
- size_reduction: sizeReduction,
171
- size_reduction_percentage: sizeCompressionRatio,
172
- information_preservation: informationPreservation,
173
- compression_efficiency: this.calculateCompressionEfficiency(compressionRatio, informationPreservation)
174
- };
175
- }
176
-
177
- /**
178
- * 성능 메트릭을 계산합니다
179
- * @param {Object} metadata - 메타데이터
180
- * @returns {Object} 성능 메트릭
181
- */
182
- calculatePerformanceMetrics(metadata) {
183
- const performance = {
184
- compression_time: metadata.processingTime || 0,
185
- decompression_time: metadata.decompressionTime || 0,
186
- memory_usage: metadata.memoryUsage || 0,
187
- throughput: 0,
188
- tokens_per_second: 0
189
- };
190
-
191
- // 처리량 계산
192
- if (performance.compression_time > 0 && metadata.originalTokens) {
193
- performance.tokens_per_second = (metadata.originalTokens / performance.compression_time) * 1000;
194
- performance.throughput = (metadata.originalTokens / performance.compression_time) * 1000;
195
- }
196
-
197
- // 메모리 효율성 계산
198
- if (performance.memory_usage > 0 && metadata.originalTokens) {
199
- performance.memory_efficiency = metadata.originalTokens / performance.memory_usage;
200
- }
201
-
202
- // 파이프라인 성능 분석
203
- if (metadata.pipelineStages) {
204
- performance.pipeline_analysis = this.analyzePipelinePerformance(metadata.pipelineStages);
205
- }
206
-
207
- return performance;
208
- }
209
-
210
- /**
211
- * 품질 메트릭을 계산합니다
212
- * @param {string} original - 원본 콘텐츠
213
- * @param {string} compressed - 압축된 콘텐츠
214
- * @param {Object} metadata - 메타데이터
215
- * @returns {Object} 품질 메트릭
216
- */
217
- calculateQualityMetrics(original, compressed, metadata) {
218
- const quality = {
219
- readability_score: this.calculateReadabilityScore(compressed),
220
- structure_preservation: this.calculateStructurePreservation(original, compressed),
221
- semantic_similarity: this.calculateSemanticSimilarity(original, compressed),
222
- information_loss: this.calculateInformationLoss(original, compressed),
223
- coherence_score: this.calculateCoherenceScore(compressed),
224
- completeness_score: this.calculateCompletenessScore(original, compressed)
225
- };
226
-
227
- // 전체 품질 점수 계산
228
- quality.overall_quality = this.calculateOverallQuality(quality);
229
-
230
- return quality;
231
- }
232
-
233
- /**
234
- * 효율성 메트릭을 계산합니다
235
- * @param {string} original - 원본 콘텐츠
236
- * @param {string} compressed - 압축된 콘텐츠
237
- * @param {Object} metadata - 메타데이터
238
- * @returns {Object} 효율성 메트릭
239
- */
240
- calculateEfficiencyMetrics(original, compressed, metadata) {
241
- const originalTokens = this.tokenCounter.countTokens(original);
242
- const compressedTokens = this.tokenCounter.countTokens(compressed);
243
- const processingTime = metadata.processingTime || 1;
244
-
245
- const efficiency = {
246
- compression_efficiency: this.calculateCompressionEfficiency(
247
- ((originalTokens - compressedTokens) / originalTokens) * 100,
248
- this.calculateInformationPreservation(original, compressed)
249
- ),
250
- resource_utilization: this.calculateResourceUtilization(metadata),
251
- cost_effectiveness: this.calculateCostEffectiveness(originalTokens, compressedTokens, processingTime),
252
- energy_efficiency: this.calculateEnergyEfficiency(originalTokens, processingTime)
253
- };
254
-
255
- return efficiency;
256
- }
257
-
258
- /**
259
- * 정보 보존률을 계산합니다
260
- * @param {string} original - 원본 콘텐츠
261
- * @param {string} compressed - 압축된 콘텐츠
262
- * @returns {number} 정보 보존률 (0-100)
263
- */
264
- calculateInformationPreservation(original, compressed) {
265
- // 키워드 보존률 계산
266
- const originalKeywords = this.extractKeywords(original);
267
- const compressedKeywords = this.extractKeywords(compressed);
268
-
269
- const preservedKeywords = originalKeywords.filter(keyword =>
270
- compressedKeywords.includes(keyword)
271
- );
272
-
273
- const keywordPreservation = originalKeywords.length > 0
274
- ? (preservedKeywords.length / originalKeywords.length) * 100
275
- : 100;
276
-
277
- // 구조 보존률 계산
278
- const structurePreservation = this.calculateStructurePreservation(original, compressed);
279
-
280
- // 의미 보존률 계산
281
- const semanticPreservation = this.calculateSemanticSimilarity(original, compressed);
282
-
283
- // 가중 평균 계산
284
- return (keywordPreservation * 0.4 + structurePreservation * 0.3 + semanticPreservation * 0.3);
285
- }
286
-
287
- /**
288
- * 키워드를 추출합니다
289
- * @param {string} text - 텍스트
290
- * @returns {Array<string>} 키워드 배열
291
- */
292
- extractKeywords(text) {
293
- const keywords = [];
294
- const words = text.toLowerCase().split(/\s+/);
295
-
296
- // 중요한 키워드 패턴
297
- const keywordPatterns = [
298
- /\b(goal|objective|task|requirement|critical|important|key|main|primary|essential)\b/g,
299
- /\b(목표|목적|태스크|요구사항|중요|핵심|주요|필수|기본|우선)\b/g
300
- ];
301
-
302
- for (const pattern of keywordPatterns) {
303
- const matches = text.match(pattern);
304
- if (matches) {
305
- keywords.push(...matches);
306
- }
307
- }
308
-
309
- // 고유 키워드만 반환
310
- return [...new Set(keywords)];
311
- }
312
-
313
- /**
314
- * 구조 보존률을 계산합니다
315
- * @param {string} original - 원본 콘텐츠
316
- * @param {string} compressed - 압축된 콘텐츠
317
- * @returns {number} 구조 보존률 (0-100)
318
- */
319
- calculateStructurePreservation(original, compressed) {
320
- const originalHeaders = (original.match(/^#{1,6}\s+.+$/gm) || []).length;
321
- const compressedHeaders = (compressed.match(/^#{1,6}\s+.+$/gm) || []).length;
322
-
323
- const originalLists = (original.match(/^\s*[-*+]\s+/gm) || []).length;
324
- const compressedLists = (compressed.match(/^\s*[-*+]\s+/gm) || []).length;
325
-
326
- const originalCodeBlocks = (original.match(/```[\s\S]*?```/g) || []).length;
327
- const compressedCodeBlocks = (compressed.match(/```[\s\S]*?```/g) || []).length;
328
-
329
- const headerPreservation = originalHeaders > 0 ? (compressedHeaders / originalHeaders) * 100 : 100;
330
- const listPreservation = originalLists > 0 ? (compressedLists / originalLists) * 100 : 100;
331
- const codePreservation = originalCodeBlocks > 0 ? (compressedCodeBlocks / originalCodeBlocks) * 100 : 100;
332
-
333
- return (headerPreservation + listPreservation + codePreservation) / 3;
334
- }
335
-
336
- /**
337
- * 의미 유사도를 계산합니다
338
- * @param {string} original - 원본 콘텐츠
339
- * @param {string} compressed - 압축된 콘텐츠
340
- * @returns {number} 의미 유사도 (0-100)
341
- */
342
- calculateSemanticSimilarity(original, compressed) {
343
- // 단순한 어휘 기반 유사도 계산
344
- const originalWords = new Set(original.toLowerCase().split(/\s+/));
345
- const compressedWords = new Set(compressed.toLowerCase().split(/\s+/));
346
-
347
- const intersection = new Set([...originalWords].filter(word => compressedWords.has(word)));
348
- const union = new Set([...originalWords, ...compressedWords]);
349
-
350
- return union.size > 0 ? (intersection.size / union.size) * 100 : 0;
351
- }
352
-
353
- /**
354
- * 정보 손실률을 계산합니다
355
- * @param {string} original - 원본 콘텐츠
356
- * @param {string} compressed - 압축된 콘텐츠
357
- * @returns {number} 정보 손실률 (0-100)
358
- */
359
- calculateInformationLoss(original, compressed) {
360
- const preservation = this.calculateInformationPreservation(original, compressed);
361
- return 100 - preservation;
362
- }
363
-
364
- /**
365
- * 가독성 점수를 계산합니다
366
- * @param {string} text - 텍스트
367
- * @returns {number} 가독성 점수 (0-100)
368
- */
369
- calculateReadabilityScore(text) {
370
- const sentences = text.split(/[.!?]+/).filter(s => s.trim().length > 0);
371
- const words = text.split(/\s+/).filter(w => w.trim().length > 0);
372
-
373
- if (sentences.length === 0 || words.length === 0) return 0;
374
-
375
- const avgWordsPerSentence = words.length / sentences.length;
376
- const avgCharsPerWord = words.reduce((sum, word) => sum + word.length, 0) / words.length;
377
-
378
- // 단순한 가독성 점수 계산
379
- let score = 100;
380
-
381
- // 문장 길이 페널티
382
- if (avgWordsPerSentence > 20) score -= 20;
383
- else if (avgWordsPerSentence > 15) score -= 10;
384
-
385
- // 단어 길이 페널티
386
- if (avgCharsPerWord > 7) score -= 20;
387
- else if (avgCharsPerWord > 5) score -= 10;
388
-
389
- return Math.max(0, Math.min(100, score));
390
- }
391
-
392
- /**
393
- * 일관성 점수를 계산합니다
394
- * @param {string} text - 텍스트
395
- * @returns {number} 일관성 점수 (0-100)
396
- */
397
- calculateCoherenceScore(text) {
398
- // 단순한 일관성 점수 계산
399
- const lines = text.split('\n');
400
- let coherenceScore = 100;
401
-
402
- // 빈 줄 패턴 확인
403
- const emptyLinePattern = /^\s*$/;
404
- const consecutiveEmptyLines = lines.reduce((count, line, index) => {
405
- if (emptyLinePattern.test(line) && index > 0 && emptyLinePattern.test(lines[index - 1])) {
406
- return count + 1;
407
- }
408
- return count;
409
- }, 0);
410
-
411
- coherenceScore -= consecutiveEmptyLines * 2;
412
-
413
- // 헤더 계층 구조 확인
414
- const headers = lines.filter(line => line.match(/^#{1,6}\s+/));
415
- const headerLevels = headers.map(header => header.match(/^(#{1,6})/)[1].length);
416
-
417
- let previousLevel = 0;
418
- for (const level of headerLevels) {
419
- if (level - previousLevel > 1) {
420
- coherenceScore -= 5; // 헤더 레벨 건너뛰기 페널티
421
- }
422
- previousLevel = level;
423
- }
424
-
425
- return Math.max(0, Math.min(100, coherenceScore));
426
- }
427
-
428
- /**
429
- * 완성도 점수를 계산합니다
430
- * @param {string} original - 원본 콘텐츠
431
- * @param {string} compressed - 압축된 콘텐츠
432
- * @returns {number} 완성도 점수 (0-100)
433
- */
434
- calculateCompletenessScore(original, compressed) {
435
- // 중요한 섹션이 유지되었는지 확인
436
- const importantSections = [
437
- 'goal', 'objective', 'requirement', 'task', 'subtask', 'acceptance criteria',
438
- '목표', '목적', '요구사항', '태스크', '하위', '승인 기준'
439
- ];
440
-
441
- let foundSections = 0;
442
- const lowerCompressed = compressed.toLowerCase();
443
-
444
- for (const section of importantSections) {
445
- if (lowerCompressed.includes(section.toLowerCase())) {
446
- foundSections++;
447
- }
448
- }
449
-
450
- const completenessRatio = foundSections / importantSections.length;
451
- return completenessRatio * 100;
452
- }
453
-
454
- /**
455
- * 압축 효율성을 계산합니다
456
- * @param {number} compressionRatio - 압축률
457
- * @param {number} informationPreservation - 정보 보존률
458
- * @returns {number} 압축 효율성 (0-100)
459
- */
460
- calculateCompressionEfficiency(compressionRatio, informationPreservation) {
461
- // 압축률과 정보 보존률의 균형을 고려한 효율성 계산
462
- const alpha = 0.6; // 압축률 가중치
463
- const beta = 0.4; // 정보 보존률 가중치
464
-
465
- return (alpha * compressionRatio) + (beta * informationPreservation);
466
- }
467
-
468
- /**
469
- * 자원 사용률을 계산합니다
470
- * @param {Object} metadata - 메타데이터
471
- * @returns {number} 자원 사용률 (0-100)
472
- */
473
- calculateResourceUtilization(metadata) {
474
- // 메모리 사용량, 처리 시간 등을 고려한 자원 사용률
475
- const memoryScore = metadata.memoryUsage ? Math.min(100, 100 - (metadata.memoryUsage / 1024 / 1024)) : 100;
476
- const timeScore = metadata.processingTime ? Math.min(100, 100 - (metadata.processingTime / 1000)) : 100;
477
-
478
- return (memoryScore + timeScore) / 2;
479
- }
480
-
481
- /**
482
- * 비용 효율성을 계산합니다
483
- * @param {number} originalTokens - 원본 토큰 수
484
- * @param {number} compressedTokens - 압축된 토큰 수
485
- * @param {number} processingTime - 처리 시간
486
- * @returns {number} 비용 효율성 (0-100)
487
- */
488
- calculateCostEffectiveness(originalTokens, compressedTokens, processingTime) {
489
- const tokenSaved = originalTokens - compressedTokens;
490
- const processingTimeInSeconds = processingTime / 1000;
491
-
492
- if (processingTimeInSeconds === 0) return 0;
493
-
494
- // 초당 절약된 토큰 수
495
- const tokensPerSecond = tokenSaved / processingTimeInSeconds;
496
-
497
- // 정규화된 비용 효율성 (최대 100)
498
- return Math.min(100, tokensPerSecond);
499
- }
500
-
501
- /**
502
- * 에너지 효율성을 계산합니다
503
- * @param {number} originalTokens - 원본 토큰 수
504
- * @param {number} processingTime - 처리 시간
505
- * @returns {number} 에너지 효율성 (0-100)
506
- */
507
- calculateEnergyEfficiency(originalTokens, processingTime) {
508
- const processingTimeInSeconds = processingTime / 1000;
509
-
510
- if (processingTimeInSeconds === 0) return 0;
511
-
512
- // 초당 처리된 토큰 수
513
- const tokensPerSecond = originalTokens / processingTimeInSeconds;
514
-
515
- // 정규화된 에너지 효율성 (최대 100)
516
- return Math.min(100, tokensPerSecond / 10);
517
- }
518
-
519
- /**
520
- * 전체 품질 점수를 계산합니다
521
- * @param {Object} quality - 품질 메트릭
522
- * @returns {number} 전체 품질 점수 (0-100)
523
- */
524
- calculateOverallQuality(quality) {
525
- const weights = {
526
- readability_score: 0.2,
527
- structure_preservation: 0.25,
528
- semantic_similarity: 0.25,
529
- coherence_score: 0.15,
530
- completeness_score: 0.15
531
- };
532
-
533
- let totalScore = 0;
534
- let totalWeight = 0;
535
-
536
- for (const [metric, weight] of Object.entries(weights)) {
537
- if (quality[metric] !== undefined) {
538
- totalScore += quality[metric] * weight;
539
- totalWeight += weight;
540
- }
541
- }
542
-
543
- return totalWeight > 0 ? totalScore / totalWeight : 0;
544
- }
545
-
546
- /**
547
- * 파이프라인 성능을 분석합니다
548
- * @param {Array<Object>} stages - 파이프라인 스테이지
549
- * @returns {Object} 파이프라인 성능 분석
550
- */
551
- analyzePipelinePerformance(stages) {
552
- const analysis = {
553
- total_stages: stages.length,
554
- total_duration: stages.reduce((sum, stage) => sum + stage.duration, 0),
555
- average_duration: 0,
556
- slowest_stage: null,
557
- fastest_stage: null,
558
- stage_breakdown: []
559
- };
560
-
561
- if (stages.length > 0) {
562
- analysis.average_duration = analysis.total_duration / stages.length;
563
-
564
- // 가장 느린/빠른 스테이지 찾기
565
- let slowest = stages[0];
566
- let fastest = stages[0];
567
-
568
- for (const stage of stages) {
569
- if (stage.duration > slowest.duration) slowest = stage;
570
- if (stage.duration < fastest.duration) fastest = stage;
571
- }
572
-
573
- analysis.slowest_stage = { name: slowest.name, duration: slowest.duration };
574
- analysis.fastest_stage = { name: fastest.name, duration: fastest.duration };
575
-
576
- // 스테이지별 세부 분석
577
- analysis.stage_breakdown = stages.map(stage => ({
578
- name: stage.name,
579
- duration: stage.duration,
580
- percentage: (stage.duration / analysis.total_duration) * 100,
581
- success: stage.success
582
- }));
583
- }
584
-
585
- return analysis;
586
- }
587
-
588
- /**
589
- * 벤치마크 분석을 수행합니다
590
- * @param {Object} metrics - 메트릭 데이터
591
- * @returns {Object} 벤치마크 분석
592
- */
593
- analyzeBenchmark(metrics) {
594
- const benchmark = {
595
- overall_grade: 'C',
596
- overall_score: 0,
597
- category_grades: {},
598
- recommendations: []
599
- };
600
-
601
- let totalScore = 0;
602
- let categoryCount = 0;
603
-
604
- // 각 카테고리별 벤치마크 분석
605
- for (const [category, categoryMetrics] of Object.entries(metrics)) {
606
- const categoryGrade = this.gradeCategoryMetrics(categoryMetrics);
607
- benchmark.category_grades[category] = categoryGrade;
608
- totalScore += categoryGrade.score;
609
- categoryCount++;
610
- }
611
-
612
- // 전체 점수 계산
613
- benchmark.overall_score = categoryCount > 0 ? totalScore / categoryCount : 0;
614
- benchmark.overall_grade = this.scoreToGrade(benchmark.overall_score);
615
-
616
- // 권장사항 생성
617
- benchmark.recommendations = this.generateRecommendations(metrics, benchmark);
618
-
619
- return benchmark;
620
- }
621
-
622
- /**
623
- * 카테고리 메트릭을 평가합니다
624
- * @param {Object} categoryMetrics - 카테고리 메트릭
625
- * @returns {Object} 카테고리 평가
626
- */
627
- gradeCategoryMetrics(categoryMetrics) {
628
- const grade = {
629
- score: 0,
630
- grade: 'F',
631
- details: {}
632
- };
633
-
634
- let totalScore = 0;
635
- let metricCount = 0;
636
-
637
- for (const [metric, value] of Object.entries(categoryMetrics)) {
638
- if (typeof value === 'number' && this.benchmarkThresholds[metric]) {
639
- const metricGrade = this.gradeMetric(metric, value);
640
- grade.details[metric] = metricGrade;
641
- totalScore += metricGrade.score;
642
- metricCount++;
643
- }
644
- }
645
-
646
- if (metricCount > 0) {
647
- grade.score = totalScore / metricCount;
648
- grade.grade = this.scoreToGrade(grade.score);
649
- }
650
-
651
- return grade;
652
- }
653
-
654
- /**
655
- * 개별 메트릭을 평가합니다
656
- * @param {string} metric - 메트릭 이름
657
- * @param {number} value - 메트릭 값
658
- * @returns {Object} 메트릭 평가
659
- */
660
- gradeMetric(metric, value) {
661
- const thresholds = this.benchmarkThresholds[metric];
662
- if (!thresholds) return { score: 50, grade: 'C' };
663
-
664
- let score = 0;
665
-
666
- if (value >= thresholds.excellent) {
667
- score = 90;
668
- } else if (value >= thresholds.good) {
669
- score = 80;
670
- } else if (value >= thresholds.acceptable) {
671
- score = 70;
672
- } else if (value >= thresholds.poor) {
673
- score = 60;
674
- } else {
675
- score = 40;
676
- }
677
-
678
- return {
679
- score,
680
- grade: this.scoreToGrade(score),
681
- value,
682
- threshold: thresholds
683
- };
684
- }
685
-
686
- /**
687
- * 점수를 등급으로 변환합니다
688
- * @param {number} score - 점수 (0-100)
689
- * @returns {string} 등급 (A-F)
690
- */
691
- scoreToGrade(score) {
692
- if (score >= 90) return 'A';
693
- if (score >= 80) return 'B';
694
- if (score >= 70) return 'C';
695
- if (score >= 60) return 'D';
696
- return 'F';
697
- }
698
-
699
- /**
700
- * 분석 결과를 생성합니다
701
- * @param {Object} metrics - 메트릭 데이터
702
- * @param {Object} benchmark - 벤치마크 분석
703
- * @returns {Object} 분석 결과
704
- */
705
- generateAnalysis(metrics, benchmark) {
706
- return {
707
- summary: this.generateSummary(metrics),
708
- strengths: this.identifyStrengths(metrics, benchmark),
709
- weaknesses: this.identifyWeaknesses(metrics, benchmark),
710
- recommendations: this.generateRecommendations(metrics, benchmark),
711
- trend_analysis: this.analyzeTrends(),
712
- comparative_analysis: this.generateComparativeAnalysis(metrics)
713
- };
714
- }
715
-
716
- /**
717
- * 요약을 생성합니다
718
- * @param {Object} metrics - 메트릭 데이터
719
- * @returns {string} 요약
720
- */
721
- generateSummary(metrics) {
722
- const compressionRatio = metrics.compression?.token_reduction_percentage || 0;
723
- const informationPreservation = metrics.compression?.information_preservation || 0;
724
- const processingTime = metrics.performance?.compression_time || 0;
725
-
726
- return `압축률 ${compressionRatio.toFixed(1)}%로 ${informationPreservation.toFixed(1)}%의 정보를 보존하며 ${processingTime}ms 내에 처리되었습니다.`;
727
- }
728
-
729
- /**
730
- * 강점을 식별합니다
731
- * @param {Object} metrics - 메트릭 데이터
732
- * @param {Object} benchmark - 벤치마크 분석
733
- * @returns {Array<string>} 강점 목록
734
- */
735
- identifyStrengths(metrics, benchmark) {
736
- const strengths = [];
737
-
738
- for (const [category, grade] of Object.entries(benchmark.category_grades)) {
739
- if (grade.score >= 80) {
740
- strengths.push(`${category} 카테고리에서 우수한 성능을 보였습니다 (${grade.grade}등급)`);
741
- }
742
- }
743
-
744
- return strengths;
745
- }
746
-
747
- /**
748
- * 약점을 식별합니다
749
- * @param {Object} metrics - 메트릭 데이터
750
- * @param {Object} benchmark - 벤치마크 분석
751
- * @returns {Array<string>} 약점 목록
752
- */
753
- identifyWeaknesses(metrics, benchmark) {
754
- const weaknesses = [];
755
-
756
- for (const [category, grade] of Object.entries(benchmark.category_grades)) {
757
- if (grade.score < 60) {
758
- weaknesses.push(`${category} 카테고리에서 개선이 필요합니다 (${grade.grade}등급)`);
759
- }
760
- }
761
-
762
- return weaknesses;
763
- }
764
-
765
- /**
766
- * 권장사항을 생성합니다
767
- * @param {Object} metrics - 메트릭 데이터
768
- * @param {Object} benchmark - 벤치마크 분석
769
- * @returns {Array<string>} 권장사항 목록
770
- */
771
- generateRecommendations(metrics, benchmark) {
772
- const recommendations = [];
773
-
774
- // 압축률 기반 권장사항
775
- const compressionRatio = metrics.compression?.token_reduction_percentage || 0;
776
- if (compressionRatio < 30) {
777
- recommendations.push('압축률을 높이기 위해 더 적극적인 압축 전략을 고려하세요.');
778
- } else if (compressionRatio > 80) {
779
- recommendations.push('압축률이 너무 높습니다. 정보 손실을 방지하기 위해 덜 적극적인 압축을 고려하세요.');
780
- }
781
-
782
- // 성능 기반 권장사항
783
- const processingTime = metrics.performance?.compression_time || 0;
784
- if (processingTime > 5000) {
785
- recommendations.push('처리 시간이 오래 걸립니다. 성능 최적화를 고려하세요.');
786
- }
787
-
788
- // 품질 기반 권장사항
789
- const informationPreservation = metrics.compression?.information_preservation || 0;
790
- if (informationPreservation < 70) {
791
- recommendations.push('정보 보존률이 낮습니다. 중요한 정보를 더 잘 보존하는 방법을 고려하세요.');
792
- }
793
-
794
- return recommendations;
795
- }
796
-
797
- /**
798
- * 트렌드를 분석합니다
799
- * @returns {Object} 트렌드 분석
800
- */
801
- analyzeTrends() {
802
- if (this.measurementHistory.length < 2) {
803
- return { message: '트렌드 분석을 위한 충분한 데이터가 없습니다.' };
804
- }
805
-
806
- const recent = this.measurementHistory.slice(-5);
807
- const trends = {};
808
-
809
- // 압축률 트렌드
810
- const compressionRatios = recent.map(m => m.metrics.compression?.token_reduction_percentage || 0);
811
- trends.compression_ratio = this.calculateTrend(compressionRatios);
812
-
813
- // 처리 시간 트렌드
814
- const processingTimes = recent.map(m => m.metrics.performance?.compression_time || 0);
815
- trends.processing_time = this.calculateTrend(processingTimes);
816
-
817
- // 품질 트렌드
818
- const qualityScores = recent.map(m => m.metrics.quality?.overall_quality || 0);
819
- trends.quality = this.calculateTrend(qualityScores);
820
-
821
- return trends;
822
- }
823
-
824
- /**
825
- * 트렌드를 계산합니다
826
- * @param {Array<number>} values - 값 배열
827
- * @returns {Object} 트렌드 정보
828
- */
829
- calculateTrend(values) {
830
- if (values.length < 2) return { trend: 'stable', change: 0 };
831
-
832
- const first = values[0];
833
- const last = values[values.length - 1];
834
- const change = ((last - first) / first) * 100;
835
-
836
- let trend = 'stable';
837
- if (change > 5) trend = 'increasing';
838
- else if (change < -5) trend = 'decreasing';
839
-
840
- return { trend, change: change.toFixed(1) };
841
- }
842
-
843
- /**
844
- * 비교 분석을 생성합니다
845
- * @param {Object} metrics - 메트릭 데이터
846
- * @returns {Object} 비교 분석
847
- */
848
- generateComparativeAnalysis(metrics) {
849
- const analysis = {
850
- vs_previous: null,
851
- vs_average: null,
852
- vs_best: null
853
- };
854
-
855
- if (this.measurementHistory.length > 1) {
856
- const current = metrics;
857
- const previous = this.measurementHistory[this.measurementHistory.length - 2].metrics;
858
-
859
- analysis.vs_previous = {
860
- compression_ratio_change: this.calculateChange(
861
- previous.compression?.token_reduction_percentage || 0,
862
- current.compression?.token_reduction_percentage || 0
863
- ),
864
- quality_change: this.calculateChange(
865
- previous.quality?.overall_quality || 0,
866
- current.quality?.overall_quality || 0
867
- ),
868
- performance_change: this.calculateChange(
869
- previous.performance?.compression_time || 0,
870
- current.performance?.compression_time || 0
871
- )
872
- };
873
- }
874
-
875
- return analysis;
876
- }
877
-
878
- /**
879
- * 변화량을 계산합니다
880
- * @param {number} previous - 이전 값
881
- * @param {number} current - 현재 값
882
- * @returns {Object} 변화량 정보
883
- */
884
- calculateChange(previous, current) {
885
- const change = current - previous;
886
- const changePercentage = previous > 0 ? (change / previous) * 100 : 0;
887
-
888
- return {
889
- absolute: change,
890
- percentage: changePercentage.toFixed(1),
891
- direction: change > 0 ? 'increase' : change < 0 ? 'decrease' : 'stable'
892
- };
893
- }
894
-
895
- /**
896
- * 측정 ID를 생성합니다
897
- * @returns {string} 측정 ID
898
- */
899
- generateMeasurementId() {
900
- const timestamp = Date.now();
901
- const random = Math.random().toString(36).substr(2, 9);
902
- return `metric_${timestamp}_${random}`;
903
- }
904
-
905
- /**
906
- * 측정 결과를 내보냅니다
907
- * @param {string} measurementId - 측정 ID
908
- * @param {string} format - 내보내기 형식 ('json', 'csv', 'report')
909
- * @returns {string} 내보내기 결과
910
- */
911
- exportMeasurement(measurementId, format = 'json') {
912
- const measurement = this.measurements.get(measurementId);
913
- if (!measurement) {
914
- throw new Error(`측정 결과를 찾을 수 없습니다: ${measurementId}`);
915
- }
916
-
917
- switch (format) {
918
- case 'json':
919
- return JSON.stringify(measurement, null, 2);
920
- case 'csv':
921
- return this.convertToCSV(measurement);
922
- case 'report':
923
- return this.generateReport(measurement);
924
- default:
925
- throw new Error(`지원되지 않는 형식: ${format}`);
926
- }
927
- }
928
-
929
- /**
930
- * CSV 형식으로 변환합니다
931
- * @param {Object} measurement - 측정 결과
932
- * @returns {string} CSV 문자열
933
- */
934
- convertToCSV(measurement) {
935
- // 플랫한 구조로 변환
936
- const flatData = this.flattenObject(measurement.metrics);
937
-
938
- const headers = Object.keys(flatData);
939
- const values = Object.values(flatData);
940
-
941
- return [headers.join(','), values.join(',')].join('\n');
942
- }
943
-
944
- /**
945
- * 객체를 평면화합니다
946
- * @param {Object} obj - 객체
947
- * @param {string} prefix - 접두사
948
- * @returns {Object} 평면화된 객체
949
- */
950
- flattenObject(obj, prefix = '') {
951
- const flattened = {};
952
-
953
- for (const [key, value] of Object.entries(obj)) {
954
- const newKey = prefix ? `${prefix}.${key}` : key;
955
-
956
- if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
957
- Object.assign(flattened, this.flattenObject(value, newKey));
958
- } else {
959
- flattened[newKey] = value;
960
- }
961
- }
962
-
963
- return flattened;
964
- }
965
-
966
- /**
967
- * 보고서를 생성합니다
968
- * @param {Object} measurement - 측정 결과
969
- * @returns {string} 보고서
970
- */
971
- generateReport(measurement) {
972
- const report = [];
973
-
974
- report.push('# 압축 성능 보고서');
975
- report.push(`측정 ID: ${measurement.id}`);
976
- report.push(`측정 시간: ${new Date(measurement.timestamp).toLocaleString()}`);
977
- report.push('');
978
-
979
- // 요약
980
- report.push('## 요약');
981
- report.push(measurement.analysis.summary);
982
- report.push('');
983
-
984
- // 벤치마크 결과
985
- report.push('## 벤치마크 결과');
986
- report.push(`전체 등급: ${measurement.benchmark.overall_grade} (${measurement.benchmark.overall_score.toFixed(1)}점)`);
987
- report.push('');
988
-
989
- // 강점과 약점
990
- if (measurement.analysis.strengths.length > 0) {
991
- report.push('## 강점');
992
- measurement.analysis.strengths.forEach(strength => {
993
- report.push(`- ${strength}`);
994
- });
995
- report.push('');
996
- }
997
-
998
- if (measurement.analysis.weaknesses.length > 0) {
999
- report.push('## 약점');
1000
- measurement.analysis.weaknesses.forEach(weakness => {
1001
- report.push(`- ${weakness}`);
1002
- });
1003
- report.push('');
1004
- }
1005
-
1006
- // 권장사항
1007
- if (measurement.analysis.recommendations.length > 0) {
1008
- report.push('## 권장사항');
1009
- measurement.analysis.recommendations.forEach(rec => {
1010
- report.push(`- ${rec}`);
1011
- });
1012
- report.push('');
1013
- }
1014
-
1015
- return report.join('\n');
1016
- }
1017
-
1018
- /**
1019
- * 리소스를 정리합니다
1020
- */
1021
- cleanup() {
1022
- if (this.tokenCounter) {
1023
- this.tokenCounter.cleanup();
1024
- }
1025
- this.measurements.clear();
1026
- this.benchmarks.clear();
1027
- this.measurementHistory.length = 0;
1028
- }
1029
- }
1030
-
1031
- export default CompressionMetrics;