gthinking 1.2.1 → 2.1.1
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/.eslintrc.js +34 -0
- package/ANALYSIS_SUMMARY.md +363 -0
- package/README.md +230 -245
- package/dist/analysis/analysis-engine.d.ts +63 -0
- package/dist/analysis/analysis-engine.d.ts.map +1 -0
- package/dist/analysis/analysis-engine.js +322 -0
- package/dist/analysis/analysis-engine.js.map +1 -0
- package/dist/core/config.d.ts +1419 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +361 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/engine.d.ts +176 -0
- package/dist/core/engine.d.ts.map +1 -0
- package/dist/core/engine.js +604 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/errors.d.ts +153 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +287 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/index.d.ts +7 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/{types.js → core/index.js} +8 -4
- package/dist/core/index.js.map +1 -0
- package/dist/core/pipeline.d.ts +121 -0
- package/dist/core/pipeline.d.ts.map +1 -0
- package/dist/core/pipeline.js +289 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/rate-limiter.d.ts +58 -0
- package/dist/core/rate-limiter.d.ts.map +1 -0
- package/dist/core/rate-limiter.js +133 -0
- package/dist/core/rate-limiter.js.map +1 -0
- package/dist/core/session-manager.d.ts +96 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +223 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/creativity/creativity-engine.d.ts +6 -0
- package/dist/creativity/creativity-engine.d.ts.map +1 -0
- package/dist/creativity/creativity-engine.js +17 -0
- package/dist/creativity/creativity-engine.js.map +1 -0
- package/dist/index.d.ts +24 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +130 -104
- package/dist/index.js.map +1 -1
- package/dist/learning/learning-engine.d.ts +6 -0
- package/dist/learning/learning-engine.d.ts.map +1 -0
- package/dist/learning/learning-engine.js +17 -0
- package/dist/learning/learning-engine.js.map +1 -0
- package/dist/llm/index.d.ts +10 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +26 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/llm-service.d.ts +109 -0
- package/dist/llm/llm-service.d.ts.map +1 -0
- package/dist/llm/llm-service.js +224 -0
- package/dist/llm/llm-service.js.map +1 -0
- package/dist/llm/providers/base.d.ts +85 -0
- package/dist/llm/providers/base.d.ts.map +1 -0
- package/dist/llm/providers/base.js +57 -0
- package/dist/llm/providers/base.js.map +1 -0
- package/dist/llm/providers/cli.d.ts +23 -0
- package/dist/llm/providers/cli.d.ts.map +1 -0
- package/dist/llm/providers/cli.js +158 -0
- package/dist/llm/providers/cli.js.map +1 -0
- package/dist/llm/providers/gemini.d.ts +30 -0
- package/dist/llm/providers/gemini.d.ts.map +1 -0
- package/dist/llm/providers/gemini.js +168 -0
- package/dist/llm/providers/gemini.js.map +1 -0
- package/dist/llm/sanitization.d.ts +50 -0
- package/dist/llm/sanitization.d.ts.map +1 -0
- package/dist/llm/sanitization.js +149 -0
- package/dist/llm/sanitization.js.map +1 -0
- package/dist/{server.d.ts.map → mcp/server.d.ts.map} +1 -1
- package/dist/mcp/server.js +108 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/planning/planning-engine.d.ts +6 -0
- package/dist/planning/planning-engine.d.ts.map +1 -0
- package/dist/planning/planning-engine.js +17 -0
- package/dist/planning/planning-engine.js.map +1 -0
- package/dist/reasoning/reasoning-engine.d.ts +6 -0
- package/dist/reasoning/reasoning-engine.d.ts.map +1 -0
- package/dist/reasoning/reasoning-engine.js +17 -0
- package/dist/reasoning/reasoning-engine.js.map +1 -0
- package/dist/search/search-engine.d.ts +99 -0
- package/dist/search/search-engine.d.ts.map +1 -0
- package/dist/search/search-engine.js +271 -0
- package/dist/search/search-engine.js.map +1 -0
- package/dist/synthesis/synthesis-engine.d.ts +6 -0
- package/dist/synthesis/synthesis-engine.d.ts.map +1 -0
- package/dist/synthesis/synthesis-engine.js +17 -0
- package/dist/synthesis/synthesis-engine.js.map +1 -0
- package/dist/types/analysis.d.ts +1534 -49
- package/dist/types/analysis.d.ts.map +1 -1
- package/dist/types/analysis.js +250 -0
- package/dist/types/analysis.js.map +1 -1
- package/dist/types/core.d.ts +257 -30
- package/dist/types/core.d.ts.map +1 -1
- package/dist/types/core.js +148 -18
- package/dist/types/core.js.map +1 -1
- package/dist/types/creativity.d.ts +2871 -56
- package/dist/types/creativity.d.ts.map +1 -1
- package/dist/types/creativity.js +195 -0
- package/dist/types/creativity.js.map +1 -1
- package/dist/types/index.d.ts +6 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +17 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/learning.d.ts +851 -61
- package/dist/types/learning.d.ts.map +1 -1
- package/dist/types/learning.js +155 -0
- package/dist/types/learning.js.map +1 -1
- package/dist/types/planning.d.ts +2223 -71
- package/dist/types/planning.d.ts.map +1 -1
- package/dist/types/planning.js +190 -0
- package/dist/types/planning.js.map +1 -1
- package/dist/types/reasoning.d.ts +2209 -72
- package/dist/types/reasoning.d.ts.map +1 -1
- package/dist/types/reasoning.js +200 -1
- package/dist/types/reasoning.js.map +1 -1
- package/dist/types/search.d.ts +981 -53
- package/dist/types/search.d.ts.map +1 -1
- package/dist/types/search.js +137 -0
- package/dist/types/search.js.map +1 -1
- package/dist/types/synthesis.d.ts +583 -37
- package/dist/types/synthesis.d.ts.map +1 -1
- package/dist/types/synthesis.js +138 -0
- package/dist/types/synthesis.js.map +1 -1
- package/dist/utils/cache.d.ts +144 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +288 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/id-generator.d.ts +89 -0
- package/dist/utils/id-generator.d.ts.map +1 -0
- package/dist/utils/id-generator.js +132 -0
- package/dist/utils/id-generator.js.map +1 -0
- package/dist/utils/index.d.ts +11 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +33 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +142 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +248 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/metrics.d.ts +149 -0
- package/dist/utils/metrics.d.ts.map +1 -0
- package/dist/utils/metrics.js +296 -0
- package/dist/utils/metrics.js.map +1 -0
- package/dist/utils/timer.d.ts +7 -0
- package/dist/utils/timer.d.ts.map +1 -0
- package/dist/utils/timer.js +17 -0
- package/dist/utils/timer.js.map +1 -0
- package/dist/utils/validation.d.ts +147 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +275 -0
- package/dist/utils/validation.js.map +1 -0
- package/docs/API.md +411 -0
- package/docs/ARCHITECTURE.md +271 -0
- package/docs/CHANGELOG.md +283 -0
- package/jest.config.js +28 -0
- package/package.json +43 -30
- package/src/analysis/analysis-engine.ts +383 -0
- package/src/core/config.ts +406 -0
- package/src/core/engine.ts +785 -0
- package/src/core/errors.ts +349 -0
- package/src/core/index.ts +12 -0
- package/src/core/pipeline.ts +424 -0
- package/src/core/rate-limiter.ts +155 -0
- package/src/core/session-manager.ts +269 -0
- package/src/creativity/creativity-engine.ts +14 -0
- package/src/index.ts +178 -0
- package/src/learning/learning-engine.ts +14 -0
- package/src/llm/index.ts +10 -0
- package/src/llm/llm-service.ts +285 -0
- package/src/llm/providers/base.ts +146 -0
- package/src/llm/providers/cli.ts +186 -0
- package/src/llm/providers/gemini.ts +201 -0
- package/src/llm/sanitization.ts +178 -0
- package/src/mcp/server.ts +117 -0
- package/src/planning/planning-engine.ts +14 -0
- package/src/reasoning/reasoning-engine.ts +14 -0
- package/src/search/search-engine.ts +333 -0
- package/src/synthesis/synthesis-engine.ts +14 -0
- package/src/types/analysis.ts +337 -0
- package/src/types/core.ts +342 -0
- package/src/types/creativity.ts +268 -0
- package/src/types/index.ts +31 -0
- package/src/types/learning.ts +215 -0
- package/src/types/planning.ts +251 -0
- package/src/types/reasoning.ts +288 -0
- package/src/types/search.ts +192 -0
- package/src/types/synthesis.ts +187 -0
- package/src/utils/cache.ts +363 -0
- package/src/utils/id-generator.ts +135 -0
- package/src/utils/index.ts +22 -0
- package/src/utils/logger.ts +290 -0
- package/src/utils/metrics.ts +380 -0
- package/src/utils/timer.ts +15 -0
- package/src/utils/validation.ts +297 -0
- package/tests/setup.ts +22 -0
- package/tests/unit/cache.test.ts +189 -0
- package/tests/unit/engine.test.ts +179 -0
- package/tests/unit/validation.test.ts +218 -0
- package/tsconfig.json +17 -12
- package/GEMINI.md +0 -68
- package/analysis.ts +0 -1063
- package/creativity.ts +0 -1055
- package/dist/analysis.d.ts +0 -54
- package/dist/analysis.d.ts.map +0 -1
- package/dist/analysis.js +0 -866
- package/dist/analysis.js.map +0 -1
- package/dist/creativity.d.ts +0 -81
- package/dist/creativity.d.ts.map +0 -1
- package/dist/creativity.js +0 -828
- package/dist/creativity.js.map +0 -1
- package/dist/engine.d.ts +0 -90
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js +0 -677
- package/dist/engine.js.map +0 -1
- package/dist/examples.d.ts +0 -7
- package/dist/examples.d.ts.map +0 -1
- package/dist/examples.js +0 -506
- package/dist/examples.js.map +0 -1
- package/dist/learning.d.ts +0 -72
- package/dist/learning.d.ts.map +0 -1
- package/dist/learning.js +0 -615
- package/dist/learning.js.map +0 -1
- package/dist/llm-service.d.ts +0 -21
- package/dist/llm-service.d.ts.map +0 -1
- package/dist/llm-service.js +0 -100
- package/dist/llm-service.js.map +0 -1
- package/dist/planning.d.ts +0 -58
- package/dist/planning.d.ts.map +0 -1
- package/dist/planning.js +0 -824
- package/dist/planning.js.map +0 -1
- package/dist/reasoning.d.ts +0 -73
- package/dist/reasoning.d.ts.map +0 -1
- package/dist/reasoning.js +0 -845
- package/dist/reasoning.js.map +0 -1
- package/dist/search-discovery.d.ts +0 -73
- package/dist/search-discovery.d.ts.map +0 -1
- package/dist/search-discovery.js +0 -548
- package/dist/search-discovery.js.map +0 -1
- package/dist/server.js +0 -113
- package/dist/server.js.map +0 -1
- package/dist/types/engine.d.ts +0 -55
- package/dist/types/engine.d.ts.map +0 -1
- package/dist/types/engine.js +0 -3
- package/dist/types/engine.js.map +0 -1
- package/dist/types.d.ts +0 -6
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/engine.ts +0 -947
- package/examples.ts +0 -717
- package/index.ts +0 -106
- package/learning.ts +0 -779
- package/llm-service.ts +0 -120
- package/planning.ts +0 -1028
- package/reasoning.ts +0 -1079
- package/search-discovery.ts +0 -700
- package/server.ts +0 -115
- package/types/analysis.ts +0 -69
- package/types/core.ts +0 -90
- package/types/creativity.ts +0 -72
- package/types/engine.ts +0 -60
- package/types/index.ts +0 -9
- package/types/learning.ts +0 -69
- package/types/planning.ts +0 -85
- package/types/reasoning.ts +0 -92
- package/types/search.ts +0 -58
- package/types/synthesis.ts +0 -42
- package/types.ts +0 -6
- /package/dist/{server.d.ts → mcp/server.d.ts} +0 -0
package/analysis.ts
DELETED
|
@@ -1,1063 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Analysis Module
|
|
3
|
-
* Deep content analysis with multi-dimensional insights extraction
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
AnalysisRequest,
|
|
8
|
-
AnalysisResult,
|
|
9
|
-
AnalysisType,
|
|
10
|
-
Finding,
|
|
11
|
-
Evidence,
|
|
12
|
-
ComparisonResult,
|
|
13
|
-
ComparisonPoint,
|
|
14
|
-
FactCheckResult,
|
|
15
|
-
SearchResult,
|
|
16
|
-
ConfidenceLevel,
|
|
17
|
-
ThinkingEvent,
|
|
18
|
-
ThinkingError,
|
|
19
|
-
ThinkingStage
|
|
20
|
-
} from './types';
|
|
21
|
-
import { EventEmitter } from 'events';
|
|
22
|
-
import { llmService } from './llm-service';
|
|
23
|
-
|
|
24
|
-
// ============================================================================
|
|
25
|
-
// SENTIMENT ANALYZER
|
|
26
|
-
// ============================================================================
|
|
27
|
-
|
|
28
|
-
interface SentimentScore {
|
|
29
|
-
positive: number;
|
|
30
|
-
negative: number;
|
|
31
|
-
neutral: number;
|
|
32
|
-
compound: number;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
class SentimentAnalyzer {
|
|
36
|
-
private positiveWords: Set<string> = new Set([
|
|
37
|
-
'excellent', 'great', 'amazing', 'wonderful', 'fantastic', 'good', 'best',
|
|
38
|
-
'love', 'like', 'happy', 'success', 'benefit', 'advantage', 'improve',
|
|
39
|
-
'progress', 'achieve', 'win', 'positive', 'effective', 'efficient'
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
private negativeWords: Set<string> = new Set([
|
|
43
|
-
'bad', 'terrible', 'awful', 'worst', 'hate', 'dislike', 'sad', 'fail',
|
|
44
|
-
'problem', 'issue', 'disadvantage', 'worse', 'decline', 'lose', 'negative',
|
|
45
|
-
'ineffective', 'inefficient', 'difficult', 'challenging', 'concern'
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
analyze(text: string): SentimentScore {
|
|
49
|
-
const words = text.toLowerCase().match(/\b\w+\b/g) || [];
|
|
50
|
-
let positive = 0;
|
|
51
|
-
let negative = 0;
|
|
52
|
-
let neutral = 0;
|
|
53
|
-
|
|
54
|
-
words.forEach(word => {
|
|
55
|
-
if (this.positiveWords.has(word)) positive++;
|
|
56
|
-
else if (this.negativeWords.has(word)) negative++;
|
|
57
|
-
else neutral++;
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
const total = words.length || 1;
|
|
61
|
-
const compound = (positive - negative) / total;
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
positive: positive / total,
|
|
65
|
-
negative: negative / total,
|
|
66
|
-
neutral: neutral / total,
|
|
67
|
-
compound
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
getSentimentLabel(score: SentimentScore): string {
|
|
72
|
-
if (score.compound > 0.5) return 'very_positive';
|
|
73
|
-
if (score.compound > 0.1) return 'positive';
|
|
74
|
-
if (score.compound < -0.5) return 'very_negative';
|
|
75
|
-
if (score.compound < -0.1) return 'negative';
|
|
76
|
-
return 'neutral';
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ============================================================================
|
|
81
|
-
// ENTITY EXTRACTOR
|
|
82
|
-
// ============================================================================
|
|
83
|
-
|
|
84
|
-
interface Entity {
|
|
85
|
-
name: string;
|
|
86
|
-
type: 'person' | 'organization' | 'location' | 'product' | 'concept' | 'event';
|
|
87
|
-
confidence: number;
|
|
88
|
-
mentions: number;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
class EntityExtractor {
|
|
92
|
-
private entityPatterns: Map<string, RegExp[]> = new Map([
|
|
93
|
-
['person', [/\b[A-Z][a-z]+ [A-Z][a-z]+\b/g, /\b(Mr|Mrs|Ms|Dr|Prof)\.? [A-Z][a-z]+\b/g]],
|
|
94
|
-
['organization', [/\b[A-Z][a-z]* (Inc|Corp|Ltd|LLC|Company|Organization)\b/g]],
|
|
95
|
-
['location', [/\b(in|at|from) ([A-Z][a-z]+( [A-Z][a-z]+)?)\b/g]],
|
|
96
|
-
['product', [/\b[A-Z][a-z]*[0-9]+\b/g, /\b(the|a|an) ([A-Z][a-z]+ (Pro|Max|Ultra|Plus))\b/gi]]
|
|
97
|
-
]);
|
|
98
|
-
|
|
99
|
-
extract(text: string): Entity[] {
|
|
100
|
-
const entities: Map<string, Entity> = new Map();
|
|
101
|
-
const words = text.split(/\s+/);
|
|
102
|
-
|
|
103
|
-
// Simple entity extraction based on patterns and capitalization
|
|
104
|
-
words.forEach((word, index) => {
|
|
105
|
-
// Check for capitalized words (potential proper nouns)
|
|
106
|
-
if (/^[A-Z][a-z]+$/.test(word) && word.length > 2) {
|
|
107
|
-
const context = words.slice(Math.max(0, index - 3), index + 4).join(' ');
|
|
108
|
-
const type = this.classifyEntity(word, context);
|
|
109
|
-
|
|
110
|
-
if (entities.has(word)) {
|
|
111
|
-
const existing = entities.get(word)!;
|
|
112
|
-
existing.mentions++;
|
|
113
|
-
existing.confidence = Math.min(0.95, existing.confidence + 0.1);
|
|
114
|
-
} else {
|
|
115
|
-
entities.set(word, {
|
|
116
|
-
name: word,
|
|
117
|
-
type,
|
|
118
|
-
confidence: 0.6,
|
|
119
|
-
mentions: 1
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
return Array.from(entities.values()).sort((a, b) => b.mentions - a.mentions);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
private classifyEntity(word: string, context: string): Entity['type'] {
|
|
129
|
-
const contextLower = context.toLowerCase();
|
|
130
|
-
|
|
131
|
-
if (/\b(said|stated|announced|CEO|founder|president)\b/i.test(contextLower)) {
|
|
132
|
-
return 'person';
|
|
133
|
-
}
|
|
134
|
-
if (/\b(company|organization|firm|corporation)\b/i.test(contextLower)) {
|
|
135
|
-
return 'organization';
|
|
136
|
-
}
|
|
137
|
-
if (/\b(in|at|from|located|city|country|region)\b/i.test(contextLower)) {
|
|
138
|
-
return 'location';
|
|
139
|
-
}
|
|
140
|
-
if (/\b(product|launched|released|device|software)\b/i.test(contextLower)) {
|
|
141
|
-
return 'product';
|
|
142
|
-
}
|
|
143
|
-
if (/\b(event|conference|meeting|summit|festival)\b/i.test(contextLower)) {
|
|
144
|
-
return 'event';
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
return 'concept';
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// ============================================================================
|
|
152
|
-
// TOPIC EXTRACTOR
|
|
153
|
-
// ============================================================================
|
|
154
|
-
|
|
155
|
-
interface Topic {
|
|
156
|
-
name: string;
|
|
157
|
-
relevance: number;
|
|
158
|
-
keywords: string[];
|
|
159
|
-
subtopics: string[];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
class TopicExtractor {
|
|
163
|
-
private stopWords: Set<string> = new Set([
|
|
164
|
-
'the', 'a', 'an', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
|
|
165
|
-
'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',
|
|
166
|
-
'should', 'may', 'might', 'must', 'can', 'this', 'that', 'these', 'those'
|
|
167
|
-
]);
|
|
168
|
-
|
|
169
|
-
extract(text: string): Topic[] {
|
|
170
|
-
const words = text.toLowerCase().match(/\b\w{4,}\b/g) || [];
|
|
171
|
-
const wordFreq = new Map<string, number>();
|
|
172
|
-
|
|
173
|
-
// Calculate word frequencies
|
|
174
|
-
words.forEach(word => {
|
|
175
|
-
if (!this.stopWords.has(word)) {
|
|
176
|
-
wordFreq.set(word, (wordFreq.get(word) || 0) + 1);
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
// Group related words into topics
|
|
181
|
-
const topics: Topic[] = [];
|
|
182
|
-
const sortedWords = Array.from(wordFreq.entries())
|
|
183
|
-
.sort((a, b) => b[1] - a[1])
|
|
184
|
-
.slice(0, 20);
|
|
185
|
-
|
|
186
|
-
// Simple topic clustering
|
|
187
|
-
const usedWords = new Set<string>();
|
|
188
|
-
sortedWords.forEach(([word, freq]) => {
|
|
189
|
-
if (!usedWords.has(word)) {
|
|
190
|
-
const relatedWords = this.findRelatedWords(word, sortedWords, usedWords);
|
|
191
|
-
topics.push({
|
|
192
|
-
name: word,
|
|
193
|
-
relevance: freq / words.length,
|
|
194
|
-
keywords: [word, ...relatedWords],
|
|
195
|
-
subtopics: relatedWords.slice(0, 3)
|
|
196
|
-
});
|
|
197
|
-
usedWords.add(word);
|
|
198
|
-
relatedWords.forEach(w => usedWords.add(w));
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
return topics.slice(0, 5);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
private findRelatedWords(
|
|
206
|
-
mainWord: string,
|
|
207
|
-
allWords: [string, number][],
|
|
208
|
-
usedWords: Set<string>
|
|
209
|
-
): string[] {
|
|
210
|
-
const related: string[] = [];
|
|
211
|
-
const mainPrefix = mainWord.substring(0, 3);
|
|
212
|
-
|
|
213
|
-
allWords.forEach(([word, _]) => {
|
|
214
|
-
if (word !== mainWord && !usedWords.has(word)) {
|
|
215
|
-
// Check for semantic similarity (simplified)
|
|
216
|
-
if (word.startsWith(mainPrefix) || mainWord.startsWith(word.substring(0, 3))) {
|
|
217
|
-
related.push(word);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
return related.slice(0, 5);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// ============================================================================
|
|
227
|
-
// KEYWORD EXTRACTOR
|
|
228
|
-
// ============================================================================
|
|
229
|
-
|
|
230
|
-
interface Keyword {
|
|
231
|
-
term: string;
|
|
232
|
-
frequency: number;
|
|
233
|
-
importance: number;
|
|
234
|
-
tfidf: number;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
class KeywordExtractor {
|
|
238
|
-
private documentFrequency: Map<string, number> = new Map();
|
|
239
|
-
private totalDocuments = 0;
|
|
240
|
-
|
|
241
|
-
extract(text: string, corpus?: string[]): Keyword[] {
|
|
242
|
-
const words = text.toLowerCase().match(/\b\w{3,}\b/g) || [];
|
|
243
|
-
const wordFreq = new Map<string, number>();
|
|
244
|
-
|
|
245
|
-
words.forEach(word => {
|
|
246
|
-
wordFreq.set(word, (wordFreq.get(word) || 0) + 1);
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// Calculate TF-IDF if corpus is provided
|
|
250
|
-
const keywords: Keyword[] = [];
|
|
251
|
-
wordFreq.forEach((freq, word) => {
|
|
252
|
-
const tf = freq / words.length;
|
|
253
|
-
const idf = corpus ? this.calculateIDF(word, corpus) : 1;
|
|
254
|
-
|
|
255
|
-
keywords.push({
|
|
256
|
-
term: word,
|
|
257
|
-
frequency: freq,
|
|
258
|
-
importance: tf * (1 + Math.log(1 + freq)),
|
|
259
|
-
tfidf: tf * idf
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
return keywords
|
|
264
|
-
.sort((a, b) => b.importance - a.importance)
|
|
265
|
-
.slice(0, 15);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private calculateIDF(word: string, corpus: string[]): number {
|
|
269
|
-
const docsWithWord = corpus.filter(doc =>
|
|
270
|
-
doc.toLowerCase().includes(word)
|
|
271
|
-
).length;
|
|
272
|
-
|
|
273
|
-
return Math.log(corpus.length / (1 + docsWithWord));
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// ============================================================================
|
|
278
|
-
// SUMMARIZER
|
|
279
|
-
// ============================================================================
|
|
280
|
-
|
|
281
|
-
class Summarizer {
|
|
282
|
-
summarize(text: string, maxLength: number = 200): string {
|
|
283
|
-
const sentences = this.splitIntoSentences(text);
|
|
284
|
-
|
|
285
|
-
if (sentences.length <= 3) {
|
|
286
|
-
return text;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Score sentences based on importance
|
|
290
|
-
const sentenceScores = sentences.map((sentence, index) => ({
|
|
291
|
-
sentence,
|
|
292
|
-
index,
|
|
293
|
-
score: this.scoreSentence(sentence, text, index, sentences.length)
|
|
294
|
-
}));
|
|
295
|
-
|
|
296
|
-
// Select top sentences
|
|
297
|
-
const topSentences = sentenceScores
|
|
298
|
-
.sort((a, b) => b.score - a.score)
|
|
299
|
-
.slice(0, Math.ceil(sentences.length * 0.3))
|
|
300
|
-
.sort((a, b) => a.index - b.index);
|
|
301
|
-
|
|
302
|
-
const summary = topSentences.map(s => s.sentence).join(' ');
|
|
303
|
-
|
|
304
|
-
return summary.length > maxLength
|
|
305
|
-
? summary.substring(0, maxLength - 3) + '...'
|
|
306
|
-
: summary;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private splitIntoSentences(text: string): string[] {
|
|
310
|
-
return text
|
|
311
|
-
.replace(/([.!?])\s+/g, "$1|")
|
|
312
|
-
.split("|")
|
|
313
|
-
.filter(s => s.trim().length > 10);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
private scoreSentence(sentence: string, fullText: string, index: number, total: number): number {
|
|
317
|
-
let score = 0;
|
|
318
|
-
|
|
319
|
-
// Position score (first and last sentences are often important)
|
|
320
|
-
if (index === 0 || index === total - 1) score += 2;
|
|
321
|
-
if (index === 1 || index === total - 2) score += 1;
|
|
322
|
-
|
|
323
|
-
// Length score (avoid very short or very long sentences)
|
|
324
|
-
const wordCount = sentence.split(/\s+/).length;
|
|
325
|
-
if (wordCount >= 8 && wordCount <= 25) score += 1;
|
|
326
|
-
|
|
327
|
-
// Keyword density score
|
|
328
|
-
const words = sentence.toLowerCase().match(/\b\w+\b/g) || [];
|
|
329
|
-
const importantWords = words.filter(w => w.length > 5);
|
|
330
|
-
score += importantWords.length / words.length;
|
|
331
|
-
|
|
332
|
-
// Presence of numerical data
|
|
333
|
-
if (/\d+/.test(sentence)) score += 0.5;
|
|
334
|
-
|
|
335
|
-
return score;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// ============================================================================
|
|
340
|
-
// FACT CHECKER
|
|
341
|
-
// ============================================================================
|
|
342
|
-
|
|
343
|
-
class FactChecker {
|
|
344
|
-
private knownFacts: Map<string, { value: boolean; confidence: number }> = new Map();
|
|
345
|
-
|
|
346
|
-
async checkClaim(claim: string, sources: SearchResult[]): Promise<FactCheckResult> {
|
|
347
|
-
// Normalize the claim
|
|
348
|
-
const normalizedClaim = claim.toLowerCase().trim();
|
|
349
|
-
|
|
350
|
-
// Check against known facts
|
|
351
|
-
if (this.knownFacts.has(normalizedClaim)) {
|
|
352
|
-
const fact = this.knownFacts.get(normalizedClaim)!;
|
|
353
|
-
return {
|
|
354
|
-
claim,
|
|
355
|
-
verdict: fact.value ? 'true' : 'false',
|
|
356
|
-
confidence: fact.confidence,
|
|
357
|
-
sources: [],
|
|
358
|
-
explanation: 'Based on verified knowledge base'
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Analyze sources for verification
|
|
363
|
-
const sourceAnalysis = this.analyzeSources(sources, claim);
|
|
364
|
-
|
|
365
|
-
// Determine verdict based on source analysis
|
|
366
|
-
let verdict: FactCheckResult['verdict'];
|
|
367
|
-
let confidence = sourceAnalysis.agreement;
|
|
368
|
-
|
|
369
|
-
if (sourceAnalysis.supporting > sourceAnalysis.contradicting * 2) {
|
|
370
|
-
verdict = 'true';
|
|
371
|
-
} else if (sourceAnalysis.contradicting > sourceAnalysis.supporting * 2) {
|
|
372
|
-
verdict = 'false';
|
|
373
|
-
} else if (sourceAnalysis.supporting > 0 || sourceAnalysis.contradicting > 0) {
|
|
374
|
-
verdict = 'partially_true';
|
|
375
|
-
confidence *= 0.7;
|
|
376
|
-
} else {
|
|
377
|
-
verdict = 'unverifiable';
|
|
378
|
-
confidence = 0.3;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
return {
|
|
382
|
-
claim,
|
|
383
|
-
verdict,
|
|
384
|
-
confidence,
|
|
385
|
-
sources: sourceAnalysis.relevantSources,
|
|
386
|
-
explanation: this.generateExplanation(verdict, sourceAnalysis),
|
|
387
|
-
corrections: sourceAnalysis.corrections
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
private analyzeSources(sources: SearchResult[], claim: string) {
|
|
392
|
-
const claimWords = claim.toLowerCase().split(/\s+/);
|
|
393
|
-
let supporting = 0;
|
|
394
|
-
let contradicting = 0;
|
|
395
|
-
let neutral = 0;
|
|
396
|
-
const relevantSources: SearchResult[] = [];
|
|
397
|
-
const corrections: string[] = [];
|
|
398
|
-
|
|
399
|
-
sources.forEach(source => {
|
|
400
|
-
const content = (source.title + ' ' + source.snippet).toLowerCase();
|
|
401
|
-
const relevance = claimWords.filter(w => content.includes(w)).length / claimWords.length;
|
|
402
|
-
|
|
403
|
-
if (relevance > 0.5) {
|
|
404
|
-
relevantSources.push(source);
|
|
405
|
-
|
|
406
|
-
// Check for supporting/contradicting indicators
|
|
407
|
-
if (this.isSupporting(content, claim)) {
|
|
408
|
-
supporting++;
|
|
409
|
-
} else if (this.isContradicting(content, claim)) {
|
|
410
|
-
contradicting++;
|
|
411
|
-
const correction = this.extractCorrection(content, claim);
|
|
412
|
-
if (correction) corrections.push(correction);
|
|
413
|
-
} else {
|
|
414
|
-
neutral++;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
const total = supporting + contradicting + neutral || 1;
|
|
420
|
-
const agreement = Math.max(supporting, contradicting) / total;
|
|
421
|
-
|
|
422
|
-
return { supporting, contradicting, neutral, agreement, relevantSources, corrections };
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
private isSupporting(content: string, _claim: string): boolean {
|
|
426
|
-
const positiveIndicators = ['confirmed', 'true', 'correct', 'yes', 'indeed', 'verified'];
|
|
427
|
-
return positiveIndicators.some(ind => content.includes(ind));
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
private isContradicting(content: string, _claim: string): boolean {
|
|
431
|
-
const negativeIndicators = ['false', 'incorrect', 'not true', 'myth', 'misleading', 'wrong'];
|
|
432
|
-
return negativeIndicators.some(ind => content.includes(ind));
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
private extractCorrection(content: string, _claim: string): string | undefined {
|
|
436
|
-
// Extract potential correction from content
|
|
437
|
-
const correctionMatch = content.match(/(?:actually|in fact|correctly|the truth is)[^.]+/i);
|
|
438
|
-
return correctionMatch ? correctionMatch[0].trim() : undefined;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
private generateExplanation(verdict: string, analysis: any): string {
|
|
442
|
-
switch (verdict) {
|
|
443
|
-
case 'true':
|
|
444
|
-
return `Supported by ${analysis.supporting} reliable sources with high agreement.`;
|
|
445
|
-
case 'false':
|
|
446
|
-
return `Contradicted by ${analysis.contradicting} sources. ${analysis.corrections.length > 0 ? 'Corrections found.' : ''}`;
|
|
447
|
-
case 'partially_true':
|
|
448
|
-
return `Mixed evidence: ${analysis.supporting} supporting, ${analysis.contradicting} contradicting sources.`;
|
|
449
|
-
case 'unverifiable':
|
|
450
|
-
return 'Insufficient reliable sources to verify this claim.';
|
|
451
|
-
default:
|
|
452
|
-
return 'Unable to determine veracity.';
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
addFact(claim: string, value: boolean, confidence: number): void {
|
|
457
|
-
this.knownFacts.set(claim.toLowerCase().trim(), { value, confidence });
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
// ============================================================================
|
|
462
|
-
// COMPARISON ENGINE
|
|
463
|
-
// ============================================================================
|
|
464
|
-
|
|
465
|
-
class ComparisonEngine {
|
|
466
|
-
compare(subjects: string[], contents: string[]): ComparisonResult {
|
|
467
|
-
const similarities: ComparisonPoint[] = [];
|
|
468
|
-
const differences: ComparisonPoint[] = [];
|
|
469
|
-
|
|
470
|
-
// Extract features from each subject
|
|
471
|
-
const features = subjects.map((subject, index) => ({
|
|
472
|
-
subject,
|
|
473
|
-
content: contents[index],
|
|
474
|
-
entities: this.extractEntities(contents[index]),
|
|
475
|
-
topics: this.extractTopics(contents[index]),
|
|
476
|
-
keywords: this.extractKeywords(contents[index]),
|
|
477
|
-
sentiment: this.analyzeSentiment(contents[index])
|
|
478
|
-
}));
|
|
479
|
-
|
|
480
|
-
// Compare features
|
|
481
|
-
const aspects = ['entities', 'topics', 'keywords', 'sentiment'];
|
|
482
|
-
|
|
483
|
-
aspects.forEach(aspect => {
|
|
484
|
-
const values: Record<string, unknown> = {};
|
|
485
|
-
features.forEach(f => {
|
|
486
|
-
values[f.subject] = f[aspect as keyof typeof f];
|
|
487
|
-
});
|
|
488
|
-
|
|
489
|
-
const similarity = this.calculateSimilarity(values);
|
|
490
|
-
|
|
491
|
-
if (similarity > 0.6) {
|
|
492
|
-
similarities.push({
|
|
493
|
-
aspect,
|
|
494
|
-
values,
|
|
495
|
-
significance: similarity
|
|
496
|
-
});
|
|
497
|
-
} else {
|
|
498
|
-
differences.push({
|
|
499
|
-
aspect,
|
|
500
|
-
values,
|
|
501
|
-
significance: 1 - similarity
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
// Generate conclusion
|
|
507
|
-
const conclusion = this.generateComparisonConclusion(subjects, similarities, differences);
|
|
508
|
-
const confidence = similarities.length / (similarities.length + differences.length);
|
|
509
|
-
|
|
510
|
-
return {
|
|
511
|
-
id: `comparison_${Date.now()}`,
|
|
512
|
-
subjects,
|
|
513
|
-
similarities,
|
|
514
|
-
differences,
|
|
515
|
-
conclusion,
|
|
516
|
-
confidence
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
private extractEntities(text: string): string[] {
|
|
521
|
-
const words = text.match(/\b[A-Z][a-z]+\b/g) || [];
|
|
522
|
-
return [...new Set(words)];
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
private extractTopics(text: string): string[] {
|
|
526
|
-
const words = text.toLowerCase().match(/\b\w{5,}\b/g) || [];
|
|
527
|
-
const freq = new Map<string, number>();
|
|
528
|
-
words.forEach(w => freq.set(w, (freq.get(w) || 0) + 1));
|
|
529
|
-
return Array.from(freq.entries())
|
|
530
|
-
.sort((a, b) => b[1] - a[1])
|
|
531
|
-
.slice(0, 5)
|
|
532
|
-
.map(([w]) => w);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
private extractKeywords(text: string): string[] {
|
|
536
|
-
return this.extractTopics(text).slice(0, 10);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
private analyzeSentiment(text: string): string {
|
|
540
|
-
const positive = /\b(good|great|excellent|positive|benefit|advantage)\b/gi;
|
|
541
|
-
const negative = /\b(bad|poor|negative|problem|issue|disadvantage)\b/gi;
|
|
542
|
-
|
|
543
|
-
const posCount = (text.match(positive) || []).length;
|
|
544
|
-
const negCount = (text.match(negative) || []).length;
|
|
545
|
-
|
|
546
|
-
if (posCount > negCount) return 'positive';
|
|
547
|
-
if (negCount > posCount) return 'negative';
|
|
548
|
-
return 'neutral';
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
private calculateSimilarity(values: Record<string, unknown>): number {
|
|
552
|
-
const subjects = Object.keys(values);
|
|
553
|
-
if (subjects.length < 2) return 1;
|
|
554
|
-
|
|
555
|
-
let totalSimilarity = 0;
|
|
556
|
-
let comparisons = 0;
|
|
557
|
-
|
|
558
|
-
for (let i = 0; i < subjects.length; i++) {
|
|
559
|
-
for (let j = i + 1; j < subjects.length; j++) {
|
|
560
|
-
const val1 = values[subjects[i]];
|
|
561
|
-
const val2 = values[subjects[j]];
|
|
562
|
-
|
|
563
|
-
if (Array.isArray(val1) && Array.isArray(val2)) {
|
|
564
|
-
const intersection = val1.filter(v => val2.includes(v));
|
|
565
|
-
const union = [...new Set([...val1, ...val2])];
|
|
566
|
-
totalSimilarity += intersection.length / union.length;
|
|
567
|
-
} else if (typeof val1 === 'string' && typeof val2 === 'string') {
|
|
568
|
-
totalSimilarity += val1 === val2 ? 1 : 0;
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
comparisons++;
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
return comparisons > 0 ? totalSimilarity / comparisons : 0;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
private generateComparisonConclusion(
|
|
579
|
-
subjects: string[],
|
|
580
|
-
similarities: ComparisonPoint[],
|
|
581
|
-
differences: ComparisonPoint[]
|
|
582
|
-
): string {
|
|
583
|
-
const similarityRatio = similarities.length / (similarities.length + differences.length);
|
|
584
|
-
|
|
585
|
-
if (similarityRatio > 0.7) {
|
|
586
|
-
return `${subjects.join(' and ')} share significant similarities, particularly in ${similarities.map(s => s.aspect).join(', ')}.`;
|
|
587
|
-
} else if (similarityRatio < 0.3) {
|
|
588
|
-
return `${subjects.join(' and ')} are notably different across ${differences.map(d => d.aspect).join(', ')}.`;
|
|
589
|
-
} else {
|
|
590
|
-
return `${subjects.join(' and ')} show both similarities and differences, with overlap in ${similarities.map(s => s.aspect).join(', ')} but divergence in ${differences.map(d => d.aspect).join(', ')}.`;
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// ============================================================================
|
|
596
|
-
// MAIN ANALYSIS ENGINE
|
|
597
|
-
// ============================================================================
|
|
598
|
-
|
|
599
|
-
export class AnalysisEngine extends EventEmitter {
|
|
600
|
-
private sentimentAnalyzer: SentimentAnalyzer;
|
|
601
|
-
private entityExtractor: EntityExtractor;
|
|
602
|
-
private topicExtractor: TopicExtractor;
|
|
603
|
-
private keywordExtractor: KeywordExtractor;
|
|
604
|
-
private summarizer: Summarizer;
|
|
605
|
-
private factChecker: FactChecker;
|
|
606
|
-
private comparisonEngine: ComparisonEngine;
|
|
607
|
-
|
|
608
|
-
constructor() {
|
|
609
|
-
super();
|
|
610
|
-
this.sentimentAnalyzer = new SentimentAnalyzer();
|
|
611
|
-
this.entityExtractor = new EntityExtractor();
|
|
612
|
-
this.topicExtractor = new TopicExtractor();
|
|
613
|
-
this.keywordExtractor = new KeywordExtractor();
|
|
614
|
-
this.summarizer = new Summarizer();
|
|
615
|
-
this.factChecker = new FactChecker();
|
|
616
|
-
this.comparisonEngine = new ComparisonEngine();
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
/**
|
|
620
|
-
* Perform comprehensive analysis on content
|
|
621
|
-
*/
|
|
622
|
-
async analyze(
|
|
623
|
-
content: string,
|
|
624
|
-
options: {
|
|
625
|
-
types?: AnalysisType[];
|
|
626
|
-
depth?: 'surface' | 'moderate' | 'deep';
|
|
627
|
-
context?: string;
|
|
628
|
-
} = {}
|
|
629
|
-
): Promise<AnalysisResult[]> {
|
|
630
|
-
const {
|
|
631
|
-
types = ['sentiment', 'entity', 'topic', 'keyword', 'summary'],
|
|
632
|
-
depth = 'moderate',
|
|
633
|
-
context
|
|
634
|
-
} = options;
|
|
635
|
-
|
|
636
|
-
const requestId = this.generateId();
|
|
637
|
-
const startTime = Date.now();
|
|
638
|
-
|
|
639
|
-
this.emit('analysis_start', {
|
|
640
|
-
id: requestId,
|
|
641
|
-
stage: ThinkingStage.ANALYSIS,
|
|
642
|
-
timestamp: new Date(),
|
|
643
|
-
data: { content: content.substring(0, 100) + '...', types }
|
|
644
|
-
} as ThinkingEvent);
|
|
645
|
-
|
|
646
|
-
const results: AnalysisResult[] = [];
|
|
647
|
-
|
|
648
|
-
// Try to get bulk analysis from LLM first
|
|
649
|
-
let llmResults: Record<string, any> = {};
|
|
650
|
-
try {
|
|
651
|
-
llmResults = await this.performLLMAnalysis(content, types, context);
|
|
652
|
-
} catch (error) {
|
|
653
|
-
console.warn('LLM analysis failed, falling back to local engines', error);
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
try {
|
|
657
|
-
for (const type of types) {
|
|
658
|
-
let result: AnalysisResult;
|
|
659
|
-
|
|
660
|
-
if (llmResults[type]) {
|
|
661
|
-
result = this.convertLLMResult(type, llmResults[type]);
|
|
662
|
-
} else {
|
|
663
|
-
result = await this.analyzeByType(type, content, context, depth);
|
|
664
|
-
}
|
|
665
|
-
results.push(result);
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
const processingTime = Date.now() - startTime;
|
|
669
|
-
|
|
670
|
-
this.emit('analysis_complete', {
|
|
671
|
-
id: requestId,
|
|
672
|
-
stage: ThinkingStage.ANALYSIS,
|
|
673
|
-
timestamp: new Date(),
|
|
674
|
-
data: { results, processingTime }
|
|
675
|
-
} as ThinkingEvent);
|
|
676
|
-
|
|
677
|
-
return results;
|
|
678
|
-
|
|
679
|
-
} catch (error) {
|
|
680
|
-
this.emit('analysis_error', {
|
|
681
|
-
id: requestId,
|
|
682
|
-
stage: ThinkingStage.ANALYSIS,
|
|
683
|
-
timestamp: new Date(),
|
|
684
|
-
data: { error }
|
|
685
|
-
} as ThinkingEvent);
|
|
686
|
-
|
|
687
|
-
throw new ThinkingError(
|
|
688
|
-
`Analysis failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
689
|
-
ThinkingStage.ANALYSIS,
|
|
690
|
-
true,
|
|
691
|
-
error instanceof Error ? error : undefined
|
|
692
|
-
);
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
private async performLLMAnalysis(content: string, types: AnalysisType[], context?: string): Promise<Record<string, any>> {
|
|
697
|
-
const prompt = `
|
|
698
|
-
Analyze this text: "${content.substring(0, 3000)}..."
|
|
699
|
-
${context ? `Context: ${context}` : ''}
|
|
700
|
-
|
|
701
|
-
Provide analysis for: ${types.join(', ')}.
|
|
702
|
-
|
|
703
|
-
Return strictly JSON with keys: ${types.map(t => `"${t}"`).join(', ')}.
|
|
704
|
-
Structure requirements:
|
|
705
|
-
- sentiment: { score: { positive: 0-1, negative: 0-1, neutral: 0-1, compound: -1 to 1 }, label: string }
|
|
706
|
-
- entity: array of { name, type, confidence: 0-1, mentions: number }
|
|
707
|
-
- topic: array of { name, relevance: 0-1, keywords: [] }
|
|
708
|
-
- keyword: array of { term, frequency, importance: 0-1 }
|
|
709
|
-
- summary: { summary: string }
|
|
710
|
-
- fact_check: array of { claim, verdict: true/false/mixed, confidence }
|
|
711
|
-
- trend: array of { trend: string, count }
|
|
712
|
-
`;
|
|
713
|
-
|
|
714
|
-
const response = await llmService.generateText({ prompt });
|
|
715
|
-
const jsonMatch = response.match(/\{[\s\S]*\}/);
|
|
716
|
-
return jsonMatch ? JSON.parse(jsonMatch[0]) : {};
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
private convertLLMResult(type: AnalysisType, data: any): AnalysisResult {
|
|
720
|
-
const id = this.generateId();
|
|
721
|
-
let findings: Finding[] = [];
|
|
722
|
-
|
|
723
|
-
// Map raw JSON data to Finding[] structure
|
|
724
|
-
if (type === 'sentiment') {
|
|
725
|
-
findings = [{
|
|
726
|
-
id: this.generateId(), type: 'sentiment_analysis',
|
|
727
|
-
value: data, confidence: 0.9, evidence: [], relatedFindings: []
|
|
728
|
-
}];
|
|
729
|
-
} else if (Array.isArray(data)) {
|
|
730
|
-
// Handle array results (entity, topic, keyword, fact_check)
|
|
731
|
-
findings = data.map((item: any) => ({
|
|
732
|
-
id: this.generateId(),
|
|
733
|
-
type,
|
|
734
|
-
value: item,
|
|
735
|
-
confidence: item.confidence || item.relevance || item.importance || 0.8,
|
|
736
|
-
evidence: [],
|
|
737
|
-
relatedFindings: []
|
|
738
|
-
}));
|
|
739
|
-
} else {
|
|
740
|
-
// Handle object results (summary)
|
|
741
|
-
findings = [{
|
|
742
|
-
id: this.generateId(), type,
|
|
743
|
-
value: data, confidence: 0.9, evidence: [], relatedFindings: []
|
|
744
|
-
}];
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
return {
|
|
748
|
-
id,
|
|
749
|
-
requestId: this.generateId(),
|
|
750
|
-
type,
|
|
751
|
-
findings,
|
|
752
|
-
confidence: 0.9,
|
|
753
|
-
processingTime: 0,
|
|
754
|
-
timestamp: new Date()
|
|
755
|
-
};
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
/**
|
|
759
|
-
* Analyze by specific type
|
|
760
|
-
*/
|
|
761
|
-
private async analyzeByType(
|
|
762
|
-
type: AnalysisType,
|
|
763
|
-
content: string,
|
|
764
|
-
context?: string,
|
|
765
|
-
depth: 'surface' | 'moderate' | 'deep' = 'moderate'
|
|
766
|
-
): Promise<AnalysisResult> {
|
|
767
|
-
const startTime = Date.now();
|
|
768
|
-
let findings: Finding[] = [];
|
|
769
|
-
|
|
770
|
-
switch (type) {
|
|
771
|
-
case 'sentiment':
|
|
772
|
-
findings = this.analyzeSentiment(content);
|
|
773
|
-
break;
|
|
774
|
-
case 'entity':
|
|
775
|
-
findings = this.analyzeEntities(content);
|
|
776
|
-
break;
|
|
777
|
-
case 'topic':
|
|
778
|
-
findings = this.analyzeTopics(content);
|
|
779
|
-
break;
|
|
780
|
-
case 'keyword':
|
|
781
|
-
findings = this.analyzeKeywords(content);
|
|
782
|
-
break;
|
|
783
|
-
case 'summary':
|
|
784
|
-
findings = this.generateSummary(content, depth);
|
|
785
|
-
break;
|
|
786
|
-
case 'fact_check':
|
|
787
|
-
findings = await this.factCheck(content);
|
|
788
|
-
break;
|
|
789
|
-
case 'comparison':
|
|
790
|
-
findings = this.compareContent(content, context);
|
|
791
|
-
break;
|
|
792
|
-
case 'trend':
|
|
793
|
-
findings = this.analyzeTrends(content);
|
|
794
|
-
break;
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
return {
|
|
798
|
-
id: this.generateId(),
|
|
799
|
-
requestId: this.generateId(),
|
|
800
|
-
type,
|
|
801
|
-
findings,
|
|
802
|
-
confidence: this.calculateConfidence(findings),
|
|
803
|
-
processingTime: Date.now() - startTime,
|
|
804
|
-
timestamp: new Date()
|
|
805
|
-
};
|
|
806
|
-
}
|
|
807
|
-
|
|
808
|
-
private analyzeSentiment(content: string): Finding[] {
|
|
809
|
-
const score = this.sentimentAnalyzer.analyze(content);
|
|
810
|
-
const label = this.sentimentAnalyzer.getSentimentLabel(score);
|
|
811
|
-
|
|
812
|
-
return [{
|
|
813
|
-
id: this.generateId(),
|
|
814
|
-
type: 'sentiment_analysis',
|
|
815
|
-
value: { score, label },
|
|
816
|
-
confidence: 0.85,
|
|
817
|
-
evidence: [{
|
|
818
|
-
source: 'sentiment_analyzer',
|
|
819
|
-
excerpt: content.substring(0, 100),
|
|
820
|
-
location: 'full_text',
|
|
821
|
-
strength: 0.8
|
|
822
|
-
}],
|
|
823
|
-
relatedFindings: []
|
|
824
|
-
}];
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
private analyzeEntities(content: string): Finding[] {
|
|
828
|
-
const entities = this.entityExtractor.extract(content);
|
|
829
|
-
|
|
830
|
-
return entities.slice(0, 10).map(entity => ({
|
|
831
|
-
id: this.generateId(),
|
|
832
|
-
type: 'entity',
|
|
833
|
-
value: entity,
|
|
834
|
-
confidence: entity.confidence,
|
|
835
|
-
evidence: [{
|
|
836
|
-
source: 'entity_extractor',
|
|
837
|
-
excerpt: `Found ${entity.name} (${entity.type}) mentioned ${entity.mentions} times`,
|
|
838
|
-
location: 'content',
|
|
839
|
-
strength: entity.confidence
|
|
840
|
-
}],
|
|
841
|
-
relatedFindings: []
|
|
842
|
-
}));
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
private analyzeTopics(content: string): Finding[] {
|
|
846
|
-
const topics = this.topicExtractor.extract(content);
|
|
847
|
-
|
|
848
|
-
return topics.map(topic => ({
|
|
849
|
-
id: this.generateId(),
|
|
850
|
-
type: 'topic',
|
|
851
|
-
value: topic,
|
|
852
|
-
confidence: topic.relevance,
|
|
853
|
-
evidence: [{
|
|
854
|
-
source: 'topic_extractor',
|
|
855
|
-
excerpt: `Topic: ${topic.name} with keywords: ${topic.keywords.join(', ')}`,
|
|
856
|
-
location: 'content',
|
|
857
|
-
strength: topic.relevance
|
|
858
|
-
}],
|
|
859
|
-
relatedFindings: []
|
|
860
|
-
}));
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
private analyzeKeywords(content: string): Finding[] {
|
|
864
|
-
const keywords = this.keywordExtractor.extract(content);
|
|
865
|
-
|
|
866
|
-
return keywords.map(kw => ({
|
|
867
|
-
id: this.generateId(),
|
|
868
|
-
type: 'keyword',
|
|
869
|
-
value: kw,
|
|
870
|
-
confidence: kw.importance,
|
|
871
|
-
evidence: [{
|
|
872
|
-
source: 'keyword_extractor',
|
|
873
|
-
excerpt: `Keyword "${kw.term}" appears ${kw.frequency} times`,
|
|
874
|
-
location: 'content',
|
|
875
|
-
strength: kw.importance
|
|
876
|
-
}],
|
|
877
|
-
relatedFindings: []
|
|
878
|
-
}));
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
private generateSummary(content: string, depth: string): Finding[] {
|
|
882
|
-
const maxLength = depth === 'surface' ? 100 : depth === 'deep' ? 500 : 250;
|
|
883
|
-
const summary = this.summarizer.summarize(content, maxLength);
|
|
884
|
-
|
|
885
|
-
return [{
|
|
886
|
-
id: this.generateId(),
|
|
887
|
-
type: 'summary',
|
|
888
|
-
value: { summary, originalLength: content.length, compressionRatio: summary.length / content.length },
|
|
889
|
-
confidence: 0.8,
|
|
890
|
-
evidence: [{
|
|
891
|
-
source: 'summarizer',
|
|
892
|
-
excerpt: summary.substring(0, 100),
|
|
893
|
-
location: 'generated_summary',
|
|
894
|
-
strength: 0.75
|
|
895
|
-
}],
|
|
896
|
-
relatedFindings: []
|
|
897
|
-
}];
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
private async factCheck(content: string): Promise<Finding[]> {
|
|
901
|
-
// Extract claims from content (simplified)
|
|
902
|
-
const sentences = content.match(/[^.!?]+[.!?]+/g) || [];
|
|
903
|
-
const claims = sentences.filter(s =>
|
|
904
|
-
s.length > 20 &&
|
|
905
|
-
!s.toLowerCase().includes('i think') &&
|
|
906
|
-
!s.toLowerCase().includes('maybe')
|
|
907
|
-
).slice(0, 3);
|
|
908
|
-
|
|
909
|
-
const findings: Finding[] = [];
|
|
910
|
-
|
|
911
|
-
for (const claim of claims) {
|
|
912
|
-
const factCheck = await this.factChecker.checkClaim(claim, []);
|
|
913
|
-
findings.push({
|
|
914
|
-
id: this.generateId(),
|
|
915
|
-
type: 'fact_check',
|
|
916
|
-
value: factCheck,
|
|
917
|
-
confidence: factCheck.confidence,
|
|
918
|
-
evidence: factCheck.sources.map(s => ({
|
|
919
|
-
source: s.url,
|
|
920
|
-
excerpt: s.snippet,
|
|
921
|
-
location: s.url,
|
|
922
|
-
strength: s.credibility
|
|
923
|
-
})),
|
|
924
|
-
relatedFindings: []
|
|
925
|
-
});
|
|
926
|
-
}
|
|
927
|
-
|
|
928
|
-
return findings;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
private compareContent(content: string, context?: string): Finding[] {
|
|
932
|
-
if (!context) {
|
|
933
|
-
return [{
|
|
934
|
-
id: this.generateId(),
|
|
935
|
-
type: 'comparison',
|
|
936
|
-
value: { error: 'No comparison context provided' },
|
|
937
|
-
confidence: 0,
|
|
938
|
-
evidence: [],
|
|
939
|
-
relatedFindings: []
|
|
940
|
-
}];
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
const comparison = this.comparisonEngine.compare(
|
|
944
|
-
['Current Content', 'Context'],
|
|
945
|
-
[content, context]
|
|
946
|
-
);
|
|
947
|
-
|
|
948
|
-
return [{
|
|
949
|
-
id: this.generateId(),
|
|
950
|
-
type: 'comparison',
|
|
951
|
-
value: comparison,
|
|
952
|
-
confidence: comparison.confidence,
|
|
953
|
-
evidence: [{
|
|
954
|
-
source: 'comparison_engine',
|
|
955
|
-
excerpt: comparison.conclusion,
|
|
956
|
-
location: 'comparison_result',
|
|
957
|
-
strength: comparison.confidence
|
|
958
|
-
}],
|
|
959
|
-
relatedFindings: []
|
|
960
|
-
}];
|
|
961
|
-
}
|
|
962
|
-
|
|
963
|
-
private analyzeTrends(content: string): Finding[] {
|
|
964
|
-
// Simple trend analysis based on temporal references
|
|
965
|
-
const temporalPatterns = [
|
|
966
|
-
{ pattern: /\b(increasing|growing|rising|upward)\b/gi, trend: 'upward' },
|
|
967
|
-
{ pattern: /\b(decreasing|declining|falling|downward)\b/gi, trend: 'downward' },
|
|
968
|
-
{ pattern: /\b(stable|constant|steady|unchanged)\b/gi, trend: 'stable' }
|
|
969
|
-
];
|
|
970
|
-
|
|
971
|
-
const trends: Array<{ trend: string; count: number }> = [];
|
|
972
|
-
|
|
973
|
-
temporalPatterns.forEach(({ pattern, trend }) => {
|
|
974
|
-
const matches = content.match(pattern) || [];
|
|
975
|
-
if (matches.length > 0) {
|
|
976
|
-
trends.push({ trend, count: matches.length });
|
|
977
|
-
}
|
|
978
|
-
});
|
|
979
|
-
|
|
980
|
-
return [{
|
|
981
|
-
id: this.generateId(),
|
|
982
|
-
type: 'trend',
|
|
983
|
-
value: trends,
|
|
984
|
-
confidence: trends.length > 0 ? 0.7 : 0.3,
|
|
985
|
-
evidence: [{
|
|
986
|
-
source: 'trend_analyzer',
|
|
987
|
-
excerpt: `Detected trends: ${trends.map(t => t.trend).join(', ')}`,
|
|
988
|
-
location: 'content',
|
|
989
|
-
strength: 0.65
|
|
990
|
-
}],
|
|
991
|
-
relatedFindings: []
|
|
992
|
-
}];
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
private calculateConfidence(findings: Finding[]): number {
|
|
996
|
-
if (findings.length === 0) return 0;
|
|
997
|
-
const avgConfidence = findings.reduce((sum, f) => sum + f.confidence, 0) / findings.length;
|
|
998
|
-
return Math.min(0.95, avgConfidence);
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
/**
|
|
1002
|
-
* Compare multiple contents
|
|
1003
|
-
*/
|
|
1004
|
-
compare(subjects: string[], contents: string[]): ComparisonResult {
|
|
1005
|
-
return this.comparisonEngine.compare(subjects, contents);
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
/**
|
|
1009
|
-
* Fact check a claim
|
|
1010
|
-
*/
|
|
1011
|
-
async factCheckClaim(claim: string, sources: SearchResult[]): Promise<FactCheckResult> {
|
|
1012
|
-
return this.factChecker.checkClaim(claim, sources);
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
/**
|
|
1016
|
-
* Add known fact for fact checking
|
|
1017
|
-
*/
|
|
1018
|
-
addKnownFact(claim: string, value: boolean, confidence: number): void {
|
|
1019
|
-
this.factChecker.addFact(claim, value, confidence);
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
private generateId(): string {
|
|
1023
|
-
return `analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
|
|
1027
|
-
// ============================================================================
|
|
1028
|
-
// EXPORT SINGLETON INSTANCE
|
|
1029
|
-
// ============================================================================
|
|
1030
|
-
|
|
1031
|
-
export const analysisEngine = new AnalysisEngine();
|
|
1032
|
-
|
|
1033
|
-
// ============================================================================
|
|
1034
|
-
// EXAMPLE USAGE
|
|
1035
|
-
// ============================================================================
|
|
1036
|
-
|
|
1037
|
-
/*
|
|
1038
|
-
// Comprehensive analysis
|
|
1039
|
-
const results = await analysisEngine.analyze(
|
|
1040
|
-
"Artificial Intelligence is revolutionizing healthcare. Machine learning algorithms can now detect diseases with 95% accuracy. " +
|
|
1041
|
-
"However, there are concerns about privacy and data security. The future looks promising but challenges remain.",
|
|
1042
|
-
{
|
|
1043
|
-
types: ['sentiment', 'entity', 'topic', 'keyword', 'summary'],
|
|
1044
|
-
depth: 'deep'
|
|
1045
|
-
}
|
|
1046
|
-
);
|
|
1047
|
-
|
|
1048
|
-
// Compare multiple texts
|
|
1049
|
-
const comparison = analysisEngine.compare(
|
|
1050
|
-
['Article A', 'Article B', 'Article C'],
|
|
1051
|
-
[
|
|
1052
|
-
"AI is transforming industries with automation and efficiency gains.",
|
|
1053
|
-
"Machine learning brings both opportunities and challenges to various sectors.",
|
|
1054
|
-
"Automation through AI technology is reshaping the workplace landscape."
|
|
1055
|
-
]
|
|
1056
|
-
);
|
|
1057
|
-
|
|
1058
|
-
// Fact check
|
|
1059
|
-
const factCheck = await analysisEngine.factCheckClaim(
|
|
1060
|
-
"AI can detect diseases with 95% accuracy",
|
|
1061
|
-
searchResults
|
|
1062
|
-
);
|
|
1063
|
-
*/
|