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.
Files changed (271) hide show
  1. package/.eslintrc.js +34 -0
  2. package/ANALYSIS_SUMMARY.md +363 -0
  3. package/README.md +230 -245
  4. package/dist/analysis/analysis-engine.d.ts +63 -0
  5. package/dist/analysis/analysis-engine.d.ts.map +1 -0
  6. package/dist/analysis/analysis-engine.js +322 -0
  7. package/dist/analysis/analysis-engine.js.map +1 -0
  8. package/dist/core/config.d.ts +1419 -0
  9. package/dist/core/config.d.ts.map +1 -0
  10. package/dist/core/config.js +361 -0
  11. package/dist/core/config.js.map +1 -0
  12. package/dist/core/engine.d.ts +176 -0
  13. package/dist/core/engine.d.ts.map +1 -0
  14. package/dist/core/engine.js +604 -0
  15. package/dist/core/engine.js.map +1 -0
  16. package/dist/core/errors.d.ts +153 -0
  17. package/dist/core/errors.d.ts.map +1 -0
  18. package/dist/core/errors.js +287 -0
  19. package/dist/core/errors.js.map +1 -0
  20. package/dist/core/index.d.ts +7 -0
  21. package/dist/core/index.d.ts.map +1 -0
  22. package/dist/{types.js → core/index.js} +8 -4
  23. package/dist/core/index.js.map +1 -0
  24. package/dist/core/pipeline.d.ts +121 -0
  25. package/dist/core/pipeline.d.ts.map +1 -0
  26. package/dist/core/pipeline.js +289 -0
  27. package/dist/core/pipeline.js.map +1 -0
  28. package/dist/core/rate-limiter.d.ts +58 -0
  29. package/dist/core/rate-limiter.d.ts.map +1 -0
  30. package/dist/core/rate-limiter.js +133 -0
  31. package/dist/core/rate-limiter.js.map +1 -0
  32. package/dist/core/session-manager.d.ts +96 -0
  33. package/dist/core/session-manager.d.ts.map +1 -0
  34. package/dist/core/session-manager.js +223 -0
  35. package/dist/core/session-manager.js.map +1 -0
  36. package/dist/creativity/creativity-engine.d.ts +6 -0
  37. package/dist/creativity/creativity-engine.d.ts.map +1 -0
  38. package/dist/creativity/creativity-engine.js +17 -0
  39. package/dist/creativity/creativity-engine.js.map +1 -0
  40. package/dist/index.d.ts +24 -32
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +130 -104
  43. package/dist/index.js.map +1 -1
  44. package/dist/learning/learning-engine.d.ts +6 -0
  45. package/dist/learning/learning-engine.d.ts.map +1 -0
  46. package/dist/learning/learning-engine.js +17 -0
  47. package/dist/learning/learning-engine.js.map +1 -0
  48. package/dist/llm/index.d.ts +10 -0
  49. package/dist/llm/index.d.ts.map +1 -0
  50. package/dist/llm/index.js +26 -0
  51. package/dist/llm/index.js.map +1 -0
  52. package/dist/llm/llm-service.d.ts +109 -0
  53. package/dist/llm/llm-service.d.ts.map +1 -0
  54. package/dist/llm/llm-service.js +224 -0
  55. package/dist/llm/llm-service.js.map +1 -0
  56. package/dist/llm/providers/base.d.ts +85 -0
  57. package/dist/llm/providers/base.d.ts.map +1 -0
  58. package/dist/llm/providers/base.js +57 -0
  59. package/dist/llm/providers/base.js.map +1 -0
  60. package/dist/llm/providers/cli.d.ts +23 -0
  61. package/dist/llm/providers/cli.d.ts.map +1 -0
  62. package/dist/llm/providers/cli.js +158 -0
  63. package/dist/llm/providers/cli.js.map +1 -0
  64. package/dist/llm/providers/gemini.d.ts +30 -0
  65. package/dist/llm/providers/gemini.d.ts.map +1 -0
  66. package/dist/llm/providers/gemini.js +168 -0
  67. package/dist/llm/providers/gemini.js.map +1 -0
  68. package/dist/llm/sanitization.d.ts +50 -0
  69. package/dist/llm/sanitization.d.ts.map +1 -0
  70. package/dist/llm/sanitization.js +149 -0
  71. package/dist/llm/sanitization.js.map +1 -0
  72. package/dist/{server.d.ts.map → mcp/server.d.ts.map} +1 -1
  73. package/dist/mcp/server.js +108 -0
  74. package/dist/mcp/server.js.map +1 -0
  75. package/dist/planning/planning-engine.d.ts +6 -0
  76. package/dist/planning/planning-engine.d.ts.map +1 -0
  77. package/dist/planning/planning-engine.js +17 -0
  78. package/dist/planning/planning-engine.js.map +1 -0
  79. package/dist/reasoning/reasoning-engine.d.ts +6 -0
  80. package/dist/reasoning/reasoning-engine.d.ts.map +1 -0
  81. package/dist/reasoning/reasoning-engine.js +17 -0
  82. package/dist/reasoning/reasoning-engine.js.map +1 -0
  83. package/dist/search/search-engine.d.ts +99 -0
  84. package/dist/search/search-engine.d.ts.map +1 -0
  85. package/dist/search/search-engine.js +271 -0
  86. package/dist/search/search-engine.js.map +1 -0
  87. package/dist/synthesis/synthesis-engine.d.ts +6 -0
  88. package/dist/synthesis/synthesis-engine.d.ts.map +1 -0
  89. package/dist/synthesis/synthesis-engine.js +17 -0
  90. package/dist/synthesis/synthesis-engine.js.map +1 -0
  91. package/dist/types/analysis.d.ts +1534 -49
  92. package/dist/types/analysis.d.ts.map +1 -1
  93. package/dist/types/analysis.js +250 -0
  94. package/dist/types/analysis.js.map +1 -1
  95. package/dist/types/core.d.ts +257 -30
  96. package/dist/types/core.d.ts.map +1 -1
  97. package/dist/types/core.js +148 -18
  98. package/dist/types/core.js.map +1 -1
  99. package/dist/types/creativity.d.ts +2871 -56
  100. package/dist/types/creativity.d.ts.map +1 -1
  101. package/dist/types/creativity.js +195 -0
  102. package/dist/types/creativity.js.map +1 -1
  103. package/dist/types/index.d.ts +6 -2
  104. package/dist/types/index.d.ts.map +1 -1
  105. package/dist/types/index.js +17 -2
  106. package/dist/types/index.js.map +1 -1
  107. package/dist/types/learning.d.ts +851 -61
  108. package/dist/types/learning.d.ts.map +1 -1
  109. package/dist/types/learning.js +155 -0
  110. package/dist/types/learning.js.map +1 -1
  111. package/dist/types/planning.d.ts +2223 -71
  112. package/dist/types/planning.d.ts.map +1 -1
  113. package/dist/types/planning.js +190 -0
  114. package/dist/types/planning.js.map +1 -1
  115. package/dist/types/reasoning.d.ts +2209 -72
  116. package/dist/types/reasoning.d.ts.map +1 -1
  117. package/dist/types/reasoning.js +200 -1
  118. package/dist/types/reasoning.js.map +1 -1
  119. package/dist/types/search.d.ts +981 -53
  120. package/dist/types/search.d.ts.map +1 -1
  121. package/dist/types/search.js +137 -0
  122. package/dist/types/search.js.map +1 -1
  123. package/dist/types/synthesis.d.ts +583 -37
  124. package/dist/types/synthesis.d.ts.map +1 -1
  125. package/dist/types/synthesis.js +138 -0
  126. package/dist/types/synthesis.js.map +1 -1
  127. package/dist/utils/cache.d.ts +144 -0
  128. package/dist/utils/cache.d.ts.map +1 -0
  129. package/dist/utils/cache.js +288 -0
  130. package/dist/utils/cache.js.map +1 -0
  131. package/dist/utils/id-generator.d.ts +89 -0
  132. package/dist/utils/id-generator.d.ts.map +1 -0
  133. package/dist/utils/id-generator.js +132 -0
  134. package/dist/utils/id-generator.js.map +1 -0
  135. package/dist/utils/index.d.ts +11 -0
  136. package/dist/utils/index.d.ts.map +1 -0
  137. package/dist/utils/index.js +33 -0
  138. package/dist/utils/index.js.map +1 -0
  139. package/dist/utils/logger.d.ts +142 -0
  140. package/dist/utils/logger.d.ts.map +1 -0
  141. package/dist/utils/logger.js +248 -0
  142. package/dist/utils/logger.js.map +1 -0
  143. package/dist/utils/metrics.d.ts +149 -0
  144. package/dist/utils/metrics.d.ts.map +1 -0
  145. package/dist/utils/metrics.js +296 -0
  146. package/dist/utils/metrics.js.map +1 -0
  147. package/dist/utils/timer.d.ts +7 -0
  148. package/dist/utils/timer.d.ts.map +1 -0
  149. package/dist/utils/timer.js +17 -0
  150. package/dist/utils/timer.js.map +1 -0
  151. package/dist/utils/validation.d.ts +147 -0
  152. package/dist/utils/validation.d.ts.map +1 -0
  153. package/dist/utils/validation.js +275 -0
  154. package/dist/utils/validation.js.map +1 -0
  155. package/docs/API.md +411 -0
  156. package/docs/ARCHITECTURE.md +271 -0
  157. package/docs/CHANGELOG.md +283 -0
  158. package/jest.config.js +28 -0
  159. package/package.json +43 -30
  160. package/src/analysis/analysis-engine.ts +383 -0
  161. package/src/core/config.ts +406 -0
  162. package/src/core/engine.ts +785 -0
  163. package/src/core/errors.ts +349 -0
  164. package/src/core/index.ts +12 -0
  165. package/src/core/pipeline.ts +424 -0
  166. package/src/core/rate-limiter.ts +155 -0
  167. package/src/core/session-manager.ts +269 -0
  168. package/src/creativity/creativity-engine.ts +14 -0
  169. package/src/index.ts +178 -0
  170. package/src/learning/learning-engine.ts +14 -0
  171. package/src/llm/index.ts +10 -0
  172. package/src/llm/llm-service.ts +285 -0
  173. package/src/llm/providers/base.ts +146 -0
  174. package/src/llm/providers/cli.ts +186 -0
  175. package/src/llm/providers/gemini.ts +201 -0
  176. package/src/llm/sanitization.ts +178 -0
  177. package/src/mcp/server.ts +117 -0
  178. package/src/planning/planning-engine.ts +14 -0
  179. package/src/reasoning/reasoning-engine.ts +14 -0
  180. package/src/search/search-engine.ts +333 -0
  181. package/src/synthesis/synthesis-engine.ts +14 -0
  182. package/src/types/analysis.ts +337 -0
  183. package/src/types/core.ts +342 -0
  184. package/src/types/creativity.ts +268 -0
  185. package/src/types/index.ts +31 -0
  186. package/src/types/learning.ts +215 -0
  187. package/src/types/planning.ts +251 -0
  188. package/src/types/reasoning.ts +288 -0
  189. package/src/types/search.ts +192 -0
  190. package/src/types/synthesis.ts +187 -0
  191. package/src/utils/cache.ts +363 -0
  192. package/src/utils/id-generator.ts +135 -0
  193. package/src/utils/index.ts +22 -0
  194. package/src/utils/logger.ts +290 -0
  195. package/src/utils/metrics.ts +380 -0
  196. package/src/utils/timer.ts +15 -0
  197. package/src/utils/validation.ts +297 -0
  198. package/tests/setup.ts +22 -0
  199. package/tests/unit/cache.test.ts +189 -0
  200. package/tests/unit/engine.test.ts +179 -0
  201. package/tests/unit/validation.test.ts +218 -0
  202. package/tsconfig.json +17 -12
  203. package/GEMINI.md +0 -68
  204. package/analysis.ts +0 -1063
  205. package/creativity.ts +0 -1055
  206. package/dist/analysis.d.ts +0 -54
  207. package/dist/analysis.d.ts.map +0 -1
  208. package/dist/analysis.js +0 -866
  209. package/dist/analysis.js.map +0 -1
  210. package/dist/creativity.d.ts +0 -81
  211. package/dist/creativity.d.ts.map +0 -1
  212. package/dist/creativity.js +0 -828
  213. package/dist/creativity.js.map +0 -1
  214. package/dist/engine.d.ts +0 -90
  215. package/dist/engine.d.ts.map +0 -1
  216. package/dist/engine.js +0 -677
  217. package/dist/engine.js.map +0 -1
  218. package/dist/examples.d.ts +0 -7
  219. package/dist/examples.d.ts.map +0 -1
  220. package/dist/examples.js +0 -506
  221. package/dist/examples.js.map +0 -1
  222. package/dist/learning.d.ts +0 -72
  223. package/dist/learning.d.ts.map +0 -1
  224. package/dist/learning.js +0 -615
  225. package/dist/learning.js.map +0 -1
  226. package/dist/llm-service.d.ts +0 -21
  227. package/dist/llm-service.d.ts.map +0 -1
  228. package/dist/llm-service.js +0 -100
  229. package/dist/llm-service.js.map +0 -1
  230. package/dist/planning.d.ts +0 -58
  231. package/dist/planning.d.ts.map +0 -1
  232. package/dist/planning.js +0 -824
  233. package/dist/planning.js.map +0 -1
  234. package/dist/reasoning.d.ts +0 -73
  235. package/dist/reasoning.d.ts.map +0 -1
  236. package/dist/reasoning.js +0 -845
  237. package/dist/reasoning.js.map +0 -1
  238. package/dist/search-discovery.d.ts +0 -73
  239. package/dist/search-discovery.d.ts.map +0 -1
  240. package/dist/search-discovery.js +0 -548
  241. package/dist/search-discovery.js.map +0 -1
  242. package/dist/server.js +0 -113
  243. package/dist/server.js.map +0 -1
  244. package/dist/types/engine.d.ts +0 -55
  245. package/dist/types/engine.d.ts.map +0 -1
  246. package/dist/types/engine.js +0 -3
  247. package/dist/types/engine.js.map +0 -1
  248. package/dist/types.d.ts +0 -6
  249. package/dist/types.d.ts.map +0 -1
  250. package/dist/types.js.map +0 -1
  251. package/engine.ts +0 -947
  252. package/examples.ts +0 -717
  253. package/index.ts +0 -106
  254. package/learning.ts +0 -779
  255. package/llm-service.ts +0 -120
  256. package/planning.ts +0 -1028
  257. package/reasoning.ts +0 -1079
  258. package/search-discovery.ts +0 -700
  259. package/server.ts +0 -115
  260. package/types/analysis.ts +0 -69
  261. package/types/core.ts +0 -90
  262. package/types/creativity.ts +0 -72
  263. package/types/engine.ts +0 -60
  264. package/types/index.ts +0 -9
  265. package/types/learning.ts +0 -69
  266. package/types/planning.ts +0 -85
  267. package/types/reasoning.ts +0 -92
  268. package/types/search.ts +0 -58
  269. package/types/synthesis.ts +0 -42
  270. package/types.ts +0 -6
  271. /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
- */