gthinking 1.0.0
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/README.md +283 -0
- package/analysis.ts +986 -0
- package/creativity.ts +1002 -0
- package/dist/analysis.d.ts +52 -0
- package/dist/analysis.d.ts.map +1 -0
- package/dist/analysis.js +792 -0
- package/dist/analysis.js.map +1 -0
- package/dist/creativity.d.ts +80 -0
- package/dist/creativity.d.ts.map +1 -0
- package/dist/creativity.js +778 -0
- package/dist/creativity.js.map +1 -0
- package/dist/engine.d.ts +76 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +675 -0
- package/dist/engine.js.map +1 -0
- package/dist/examples.d.ts +7 -0
- package/dist/examples.d.ts.map +1 -0
- package/dist/examples.js +506 -0
- package/dist/examples.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/dist/learning.d.ts +72 -0
- package/dist/learning.d.ts.map +1 -0
- package/dist/learning.js +615 -0
- package/dist/learning.js.map +1 -0
- package/dist/planning.d.ts +58 -0
- package/dist/planning.d.ts.map +1 -0
- package/dist/planning.js +824 -0
- package/dist/planning.js.map +1 -0
- package/dist/reasoning.d.ts +72 -0
- package/dist/reasoning.d.ts.map +1 -0
- package/dist/reasoning.js +792 -0
- package/dist/reasoning.js.map +1 -0
- package/dist/search-discovery.d.ts +73 -0
- package/dist/search-discovery.d.ts.map +1 -0
- package/dist/search-discovery.js +505 -0
- package/dist/search-discovery.js.map +1 -0
- package/dist/types.d.ts +535 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +77 -0
- package/dist/types.js.map +1 -0
- package/engine.ts +928 -0
- package/examples.ts +717 -0
- package/index.ts +106 -0
- package/learning.ts +779 -0
- package/package.json +51 -0
- package/planning.ts +1028 -0
- package/reasoning.ts +1019 -0
- package/search-discovery.ts +654 -0
- package/tsconfig.json +25 -0
- package/types.ts +674 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search & Discovery Module
|
|
3
|
+
* Real-time web search with multi-source aggregation and intelligent filtering
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
SearchQuery,
|
|
8
|
+
SearchResult,
|
|
9
|
+
SearchSession,
|
|
10
|
+
SearchFilters,
|
|
11
|
+
AggregatedResult,
|
|
12
|
+
SourceType,
|
|
13
|
+
ResultMetadata,
|
|
14
|
+
ConfidenceLevel,
|
|
15
|
+
ThinkingEvent,
|
|
16
|
+
ThinkingError,
|
|
17
|
+
ThinkingStage
|
|
18
|
+
} from './types';
|
|
19
|
+
import { EventEmitter } from 'events';
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// SEARCH PROVIDER INTERFACES
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
interface SearchProvider {
|
|
26
|
+
name: string;
|
|
27
|
+
search(query: string, filters: SearchFilters, maxResults: number): Promise<SearchResult[]>;
|
|
28
|
+
getCredibilityScore(url: string): number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// WEB SEARCH PROVIDER IMPLEMENTATION
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
class WebSearchProvider implements SearchProvider {
|
|
36
|
+
name = 'web_search';
|
|
37
|
+
|
|
38
|
+
async search(
|
|
39
|
+
query: string,
|
|
40
|
+
filters: SearchFilters,
|
|
41
|
+
maxResults: number
|
|
42
|
+
): Promise<SearchResult[]> {
|
|
43
|
+
// In real implementation, this would call actual search APIs
|
|
44
|
+
// For demonstration, we simulate search results
|
|
45
|
+
const simulatedResults: SearchResult[] = [];
|
|
46
|
+
|
|
47
|
+
// Generate diverse simulated results based on query
|
|
48
|
+
const topics = this.extractTopics(query);
|
|
49
|
+
|
|
50
|
+
for (let i = 0; i < maxResults; i++) {
|
|
51
|
+
const topic = topics[i % topics.length];
|
|
52
|
+
const result: SearchResult = {
|
|
53
|
+
id: `web_${Date.now()}_${i}`,
|
|
54
|
+
title: this.generateTitle(topic, i),
|
|
55
|
+
url: `https://example.com/article/${topic.replace(/\s+/g, '-')}-${i}`,
|
|
56
|
+
snippet: this.generateSnippet(topic, query),
|
|
57
|
+
source: SourceType.WEB,
|
|
58
|
+
credibility: this.calculateCredibility(i),
|
|
59
|
+
relevance: this.calculateRelevance(query, topic, i),
|
|
60
|
+
timestamp: new Date(),
|
|
61
|
+
metadata: this.generateMetadata(topic)
|
|
62
|
+
};
|
|
63
|
+
simulatedResults.push(result);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this.applyFilters(simulatedResults, filters);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
getCredibilityScore(url: string): number {
|
|
70
|
+
const credibleDomains = [
|
|
71
|
+
'edu', 'gov', 'ac.uk', 'ac.jp', 'arxiv.org',
|
|
72
|
+
'nature.com', 'science.org', 'ieee.org', 'acm.org'
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
const lowCredibilityDomains = [
|
|
76
|
+
'blogspot', 'wordpress', 'medium', 'wix', 'weebly'
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
if (credibleDomains.some(d => url.includes(d))) return 0.9;
|
|
80
|
+
if (lowCredibilityDomains.some(d => url.includes(d))) return 0.4;
|
|
81
|
+
return 0.6;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
private extractTopics(query: string): string[] {
|
|
85
|
+
// Simple topic extraction - in real implementation, use NLP
|
|
86
|
+
const commonWords = ['what', 'how', 'why', 'when', 'where', 'who', 'is', 'are', 'the', 'a', 'an'];
|
|
87
|
+
return query
|
|
88
|
+
.toLowerCase()
|
|
89
|
+
.split(/\s+/)
|
|
90
|
+
.filter(word => !commonWords.includes(word) && word.length > 3);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private generateTitle(topic: string, index: number): string {
|
|
94
|
+
const templates = [
|
|
95
|
+
`Complete Guide to ${topic}`,
|
|
96
|
+
`${topic}: Everything You Need to Know`,
|
|
97
|
+
`Understanding ${topic} in Depth`,
|
|
98
|
+
`The Future of ${topic}`,
|
|
99
|
+
`${topic} Best Practices and Strategies`,
|
|
100
|
+
`Advanced ${topic} Techniques`,
|
|
101
|
+
`${topic} Case Studies and Examples`,
|
|
102
|
+
`Breaking Down ${topic}: A Comprehensive Analysis`
|
|
103
|
+
];
|
|
104
|
+
return templates[index % templates.length];
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private generateSnippet(topic: string, query: string): string {
|
|
108
|
+
return `This comprehensive resource about ${topic} provides detailed insights related to "${query}". ` +
|
|
109
|
+
`It covers key concepts, practical applications, and expert perspectives on the subject matter.`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private calculateCredibility(index: number): number {
|
|
113
|
+
// Higher ranked results typically have higher credibility
|
|
114
|
+
return Math.max(0.3, 0.95 - (index * 0.05));
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private calculateRelevance(query: string, topic: string, index: number): number {
|
|
118
|
+
const queryWords = query.toLowerCase().split(/\s+/);
|
|
119
|
+
const topicWords = topic.toLowerCase().split(/\s+/);
|
|
120
|
+
const matchCount = topicWords.filter(w => queryWords.includes(w)).length;
|
|
121
|
+
const baseRelevance = matchCount / Math.max(queryWords.length, topicWords.length);
|
|
122
|
+
return Math.min(0.95, baseRelevance + (0.9 - index * 0.1));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private generateMetadata(topic: string): ResultMetadata {
|
|
126
|
+
return {
|
|
127
|
+
author: `Expert on ${topic}`,
|
|
128
|
+
publishDate: new Date(Date.now() - Math.random() * 365 * 24 * 60 * 60 * 1000),
|
|
129
|
+
wordCount: Math.floor(1000 + Math.random() * 4000),
|
|
130
|
+
readingTime: Math.floor(5 + Math.random() * 20),
|
|
131
|
+
relatedTopics: [topic, 'related-concept-1', 'related-concept-2'],
|
|
132
|
+
citations: Math.floor(Math.random() * 50)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private applyFilters(results: SearchResult[], filters: SearchFilters): SearchResult[] {
|
|
137
|
+
return results.filter(result => {
|
|
138
|
+
if (filters.minCredibility && result.credibility < filters.minCredibility) {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
if (filters.domains && filters.domains.length > 0) {
|
|
142
|
+
const domainMatch = filters.domains.some(d => result.url.includes(d));
|
|
143
|
+
if (!domainMatch) return false;
|
|
144
|
+
}
|
|
145
|
+
if (filters.excludeDomains) {
|
|
146
|
+
const excluded = filters.excludeDomains.some(d => result.url.includes(d));
|
|
147
|
+
if (excluded) return false;
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// KNOWLEDGE BASE SEARCH PROVIDER
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
class KnowledgeBaseProvider implements SearchProvider {
|
|
159
|
+
name = 'knowledge_base';
|
|
160
|
+
private knowledgeStore: Map<string, unknown> = new Map();
|
|
161
|
+
|
|
162
|
+
async search(
|
|
163
|
+
query: string,
|
|
164
|
+
filters: SearchFilters,
|
|
165
|
+
maxResults: number
|
|
166
|
+
): Promise<SearchResult[]> {
|
|
167
|
+
const results: SearchResult[] = [];
|
|
168
|
+
const queryLower = query.toLowerCase();
|
|
169
|
+
|
|
170
|
+
// Search through stored knowledge
|
|
171
|
+
for (const [key, value] of this.knowledgeStore.entries()) {
|
|
172
|
+
if (key.toLowerCase().includes(queryLower)) {
|
|
173
|
+
results.push({
|
|
174
|
+
id: `kb_${key}`,
|
|
175
|
+
title: key,
|
|
176
|
+
url: `knowledge://${key}`,
|
|
177
|
+
snippet: JSON.stringify(value).substring(0, 200),
|
|
178
|
+
source: SourceType.KNOWLEDGE_BASE,
|
|
179
|
+
credibility: 0.95,
|
|
180
|
+
relevance: 0.9,
|
|
181
|
+
timestamp: new Date(),
|
|
182
|
+
metadata: { wordCount: JSON.stringify(value).length }
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
if (results.length >= maxResults) break;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return results;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
getCredibilityScore(): number {
|
|
192
|
+
return 0.95; // Knowledge base has high credibility
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
addKnowledge(key: string, value: unknown): void {
|
|
196
|
+
this.knowledgeStore.set(key, value);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
getKnowledge(key: string): unknown {
|
|
200
|
+
return this.knowledgeStore.get(key);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// CACHE SEARCH PROVIDER
|
|
206
|
+
// ============================================================================
|
|
207
|
+
|
|
208
|
+
class CacheProvider implements SearchProvider {
|
|
209
|
+
name = 'cache';
|
|
210
|
+
private cache: Map<string, { result: SearchResult; timestamp: number; ttl: number }> = new Map();
|
|
211
|
+
private defaultTTL = 3600000; // 1 hour
|
|
212
|
+
|
|
213
|
+
async search(
|
|
214
|
+
query: string,
|
|
215
|
+
_filters: SearchFilters,
|
|
216
|
+
maxResults: number
|
|
217
|
+
): Promise<SearchResult[]> {
|
|
218
|
+
const results: SearchResult[] = [];
|
|
219
|
+
const now = Date.now();
|
|
220
|
+
|
|
221
|
+
for (const [key, cached] of this.cache.entries()) {
|
|
222
|
+
if (key.includes(query) && now - cached.timestamp < cached.ttl) {
|
|
223
|
+
results.push(cached.result);
|
|
224
|
+
}
|
|
225
|
+
if (results.length >= maxResults) break;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return results;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
getCredibilityScore(): number {
|
|
232
|
+
return 0.8;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
cacheResult(query: string, result: SearchResult, ttl?: number): void {
|
|
236
|
+
this.cache.set(query, {
|
|
237
|
+
result,
|
|
238
|
+
timestamp: Date.now(),
|
|
239
|
+
ttl: ttl || this.defaultTTL
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
invalidate(query: string): void {
|
|
244
|
+
this.cache.delete(query);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
clear(): void {
|
|
248
|
+
this.cache.clear();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ============================================================================
|
|
253
|
+
// MAIN SEARCH & DISCOVERY ENGINE
|
|
254
|
+
// ============================================================================
|
|
255
|
+
|
|
256
|
+
export class SearchDiscoveryEngine extends EventEmitter {
|
|
257
|
+
private providers: Map<SourceType, SearchProvider> = new Map();
|
|
258
|
+
private sessions: Map<string, SearchSession> = new Map();
|
|
259
|
+
private knowledgeBase: KnowledgeBaseProvider;
|
|
260
|
+
private cache: CacheProvider;
|
|
261
|
+
|
|
262
|
+
constructor() {
|
|
263
|
+
super();
|
|
264
|
+
this.knowledgeBase = new KnowledgeBaseProvider();
|
|
265
|
+
this.cache = new CacheProvider();
|
|
266
|
+
this.registerDefaultProviders();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
private registerDefaultProviders(): void {
|
|
270
|
+
this.providers.set(SourceType.WEB, new WebSearchProvider());
|
|
271
|
+
this.providers.set(SourceType.KNOWLEDGE_BASE, this.knowledgeBase);
|
|
272
|
+
this.providers.set(SourceType.CACHE, this.cache);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Execute a search query across multiple sources
|
|
277
|
+
*/
|
|
278
|
+
async search(
|
|
279
|
+
query: string,
|
|
280
|
+
options: {
|
|
281
|
+
sources?: SourceType[];
|
|
282
|
+
filters?: SearchFilters;
|
|
283
|
+
maxResults?: number;
|
|
284
|
+
timeout?: number;
|
|
285
|
+
} = {}
|
|
286
|
+
): Promise<SearchResult[]> {
|
|
287
|
+
const {
|
|
288
|
+
sources = [SourceType.WEB, SourceType.KNOWLEDGE_BASE, SourceType.CACHE],
|
|
289
|
+
filters = {},
|
|
290
|
+
maxResults = 10,
|
|
291
|
+
timeout = 30000
|
|
292
|
+
} = options;
|
|
293
|
+
|
|
294
|
+
const searchId = this.generateId();
|
|
295
|
+
const startTime = Date.now();
|
|
296
|
+
|
|
297
|
+
this.emit('search_start', {
|
|
298
|
+
id: searchId,
|
|
299
|
+
type: 'stage_start',
|
|
300
|
+
stage: ThinkingStage.SEARCH,
|
|
301
|
+
timestamp: new Date(),
|
|
302
|
+
data: { query, sources },
|
|
303
|
+
sessionId: searchId
|
|
304
|
+
} as ThinkingEvent);
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
// Check cache first
|
|
308
|
+
const cachedResults = await this.searchWithProvider(
|
|
309
|
+
SourceType.CACHE,
|
|
310
|
+
query,
|
|
311
|
+
filters,
|
|
312
|
+
maxResults
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
if (cachedResults.length > 0) {
|
|
316
|
+
this.emit('search_complete', {
|
|
317
|
+
id: searchId,
|
|
318
|
+
type: 'search_result',
|
|
319
|
+
stage: ThinkingStage.SEARCH,
|
|
320
|
+
timestamp: new Date(),
|
|
321
|
+
data: { results: cachedResults, fromCache: true },
|
|
322
|
+
sessionId: searchId
|
|
323
|
+
} as ThinkingEvent);
|
|
324
|
+
return cachedResults;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Execute searches across all specified sources in parallel
|
|
328
|
+
const searchPromises = sources
|
|
329
|
+
.filter(s => s !== SourceType.CACHE) // Already checked cache
|
|
330
|
+
.map(source => this.searchWithProvider(source, query, filters, maxResults));
|
|
331
|
+
|
|
332
|
+
const resultsArrays = await Promise.allSettled(searchPromises);
|
|
333
|
+
|
|
334
|
+
let allResults: SearchResult[] = [];
|
|
335
|
+
resultsArrays.forEach((result, index) => {
|
|
336
|
+
if (result.status === 'fulfilled') {
|
|
337
|
+
allResults = allResults.concat(result.value);
|
|
338
|
+
} else {
|
|
339
|
+
console.warn(`Search from ${sources[index]} failed:`, result.reason);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// Rank and filter results
|
|
344
|
+
const rankedResults = this.rankResults(allResults, query).slice(0, maxResults);
|
|
345
|
+
|
|
346
|
+
// Cache top results
|
|
347
|
+
rankedResults.forEach(result => {
|
|
348
|
+
this.cache.cacheResult(query, result);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
const processingTime = Date.now() - startTime;
|
|
352
|
+
|
|
353
|
+
this.emit('search_complete', {
|
|
354
|
+
id: searchId,
|
|
355
|
+
type: 'search_result',
|
|
356
|
+
stage: ThinkingStage.SEARCH,
|
|
357
|
+
timestamp: new Date(),
|
|
358
|
+
data: {
|
|
359
|
+
results: rankedResults,
|
|
360
|
+
fromCache: false,
|
|
361
|
+
processingTime,
|
|
362
|
+
sourcesQueried: sources.length
|
|
363
|
+
},
|
|
364
|
+
sessionId: searchId
|
|
365
|
+
} as ThinkingEvent);
|
|
366
|
+
|
|
367
|
+
return rankedResults;
|
|
368
|
+
|
|
369
|
+
} catch (error) {
|
|
370
|
+
this.emit('search_error', {
|
|
371
|
+
id: searchId,
|
|
372
|
+
type: 'stage_error',
|
|
373
|
+
stage: ThinkingStage.SEARCH,
|
|
374
|
+
timestamp: new Date(),
|
|
375
|
+
data: { error },
|
|
376
|
+
sessionId: searchId
|
|
377
|
+
} as ThinkingEvent);
|
|
378
|
+
|
|
379
|
+
throw new ThinkingError(
|
|
380
|
+
`Search failed: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
381
|
+
ThinkingStage.SEARCH,
|
|
382
|
+
true,
|
|
383
|
+
error instanceof Error ? error : undefined
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Multi-query search for comprehensive coverage
|
|
390
|
+
*/
|
|
391
|
+
async multiSearch(
|
|
392
|
+
queries: string[],
|
|
393
|
+
options: {
|
|
394
|
+
sources?: SourceType[];
|
|
395
|
+
filters?: SearchFilters;
|
|
396
|
+
maxResultsPerQuery?: number;
|
|
397
|
+
aggregate?: boolean;
|
|
398
|
+
} = {}
|
|
399
|
+
): Promise<SearchResult[] | AggregatedResult[]> {
|
|
400
|
+
const { aggregate = true, maxResultsPerQuery = 5 } = options;
|
|
401
|
+
|
|
402
|
+
const sessionId = this.generateId();
|
|
403
|
+
const session: SearchSession = {
|
|
404
|
+
id: sessionId,
|
|
405
|
+
queries: [],
|
|
406
|
+
results: [],
|
|
407
|
+
aggregatedResults: [],
|
|
408
|
+
startTime: new Date()
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
this.sessions.set(sessionId, session);
|
|
412
|
+
|
|
413
|
+
// Execute all queries
|
|
414
|
+
const searchPromises = queries.map(async (query) => {
|
|
415
|
+
const results = await this.search(query, {
|
|
416
|
+
...options,
|
|
417
|
+
maxResults: maxResultsPerQuery
|
|
418
|
+
});
|
|
419
|
+
return { query, results };
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
const searchResults = await Promise.all(searchPromises);
|
|
423
|
+
|
|
424
|
+
// Collect all results
|
|
425
|
+
searchResults.forEach(({ query, results }) => {
|
|
426
|
+
session.queries.push({
|
|
427
|
+
id: this.generateId(),
|
|
428
|
+
query,
|
|
429
|
+
sources: options.sources || [SourceType.WEB],
|
|
430
|
+
filters: options.filters || {},
|
|
431
|
+
maxResults: maxResultsPerQuery,
|
|
432
|
+
timeout: 30000,
|
|
433
|
+
timestamp: new Date()
|
|
434
|
+
});
|
|
435
|
+
session.results.push(...results);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
if (aggregate) {
|
|
439
|
+
session.aggregatedResults = this.aggregateResults(session.results);
|
|
440
|
+
return session.aggregatedResults;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return session.results;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Aggregate results by topic and identify consensus/conflicts
|
|
448
|
+
*/
|
|
449
|
+
aggregateResults(results: SearchResult[]): AggregatedResult[] {
|
|
450
|
+
const topicMap = new Map<string, SearchResult[]>();
|
|
451
|
+
|
|
452
|
+
// Group by topic (simplified - in real implementation use NLP clustering)
|
|
453
|
+
results.forEach(result => {
|
|
454
|
+
const topic = this.extractMainTopic(result.title);
|
|
455
|
+
if (!topicMap.has(topic)) {
|
|
456
|
+
topicMap.set(topic, []);
|
|
457
|
+
}
|
|
458
|
+
topicMap.get(topic)!.push(result);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const aggregated: AggregatedResult[] = [];
|
|
462
|
+
|
|
463
|
+
topicMap.forEach((topicResults, topic) => {
|
|
464
|
+
// Calculate consensus score
|
|
465
|
+
const avgCredibility = topicResults.reduce((sum, r) => sum + r.credibility, 0) / topicResults.length;
|
|
466
|
+
const avgRelevance = topicResults.reduce((sum, r) => sum + r.relevance, 0) / topicResults.length;
|
|
467
|
+
const consensusScore = (avgCredibility + avgRelevance) / 2;
|
|
468
|
+
|
|
469
|
+
// Check for conflicting information
|
|
470
|
+
const conflictingInfo = this.detectConflicts(topicResults);
|
|
471
|
+
|
|
472
|
+
// Extract key insights
|
|
473
|
+
const keyInsights = this.extractKeyInsights(topicResults);
|
|
474
|
+
|
|
475
|
+
aggregated.push({
|
|
476
|
+
topic,
|
|
477
|
+
results: topicResults,
|
|
478
|
+
consensusScore,
|
|
479
|
+
conflictingInfo,
|
|
480
|
+
keyInsights
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Sort by consensus score
|
|
485
|
+
return aggregated.sort((a, b) => b.consensusScore - a.consensusScore);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Rank results based on multiple factors
|
|
490
|
+
*/
|
|
491
|
+
private rankResults(results: SearchResult[], query: string): SearchResult[] {
|
|
492
|
+
return results
|
|
493
|
+
.map(result => ({
|
|
494
|
+
...result,
|
|
495
|
+
// Calculate composite score
|
|
496
|
+
compositeScore: this.calculateCompositeScore(result, query)
|
|
497
|
+
}))
|
|
498
|
+
.sort((a, b) => (b as any).compositeScore - (a as any).compositeScore);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private calculateCompositeScore(result: SearchResult, query: string): number {
|
|
502
|
+
const relevanceWeight = 0.4;
|
|
503
|
+
const credibilityWeight = 0.3;
|
|
504
|
+
const freshnessWeight = 0.15;
|
|
505
|
+
const engagementWeight = 0.15;
|
|
506
|
+
|
|
507
|
+
// Relevance score
|
|
508
|
+
const relevanceScore = result.relevance;
|
|
509
|
+
|
|
510
|
+
// Credibility score
|
|
511
|
+
const credibilityScore = result.credibility;
|
|
512
|
+
|
|
513
|
+
// Freshness score (newer is better)
|
|
514
|
+
const ageInDays = (Date.now() - result.timestamp.getTime()) / (1000 * 60 * 60 * 24);
|
|
515
|
+
const freshnessScore = Math.max(0, 1 - ageInDays / 365);
|
|
516
|
+
|
|
517
|
+
// Engagement score (based on citations, reading time)
|
|
518
|
+
const engagementScore = Math.min(1, (result.metadata.citations || 0) / 100);
|
|
519
|
+
|
|
520
|
+
return (
|
|
521
|
+
relevanceScore * relevanceWeight +
|
|
522
|
+
credibilityScore * credibilityWeight +
|
|
523
|
+
freshnessScore * freshnessWeight +
|
|
524
|
+
engagementScore * engagementWeight
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Search with a specific provider
|
|
530
|
+
*/
|
|
531
|
+
private async searchWithProvider(
|
|
532
|
+
source: SourceType,
|
|
533
|
+
query: string,
|
|
534
|
+
filters: SearchFilters,
|
|
535
|
+
maxResults: number
|
|
536
|
+
): Promise<SearchResult[]> {
|
|
537
|
+
const provider = this.providers.get(source);
|
|
538
|
+
if (!provider) {
|
|
539
|
+
throw new Error(`No provider registered for source: ${source}`);
|
|
540
|
+
}
|
|
541
|
+
return provider.search(query, filters, maxResults);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Add custom search provider
|
|
546
|
+
*/
|
|
547
|
+
registerProvider(source: SourceType, provider: SearchProvider): void {
|
|
548
|
+
this.providers.set(source, provider);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Add knowledge to knowledge base
|
|
553
|
+
*/
|
|
554
|
+
addKnowledge(key: string, value: unknown): void {
|
|
555
|
+
this.knowledgeBase.addKnowledge(key, value);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Get search session by ID
|
|
560
|
+
*/
|
|
561
|
+
getSession(sessionId: string): SearchSession | undefined {
|
|
562
|
+
return this.sessions.get(sessionId);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Clear all sessions
|
|
567
|
+
*/
|
|
568
|
+
clearSessions(): void {
|
|
569
|
+
this.sessions.clear();
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// Helper methods
|
|
573
|
+
private generateId(): string {
|
|
574
|
+
return `search_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
private extractMainTopic(title: string): string {
|
|
578
|
+
// Simplified topic extraction
|
|
579
|
+
const words = title.toLowerCase().split(/\s+/);
|
|
580
|
+
const significantWords = words.filter(w => w.length > 4);
|
|
581
|
+
return significantWords.slice(0, 3).join(' ') || 'general';
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
private detectConflicts(results: SearchResult[]): boolean {
|
|
585
|
+
// Simplified conflict detection
|
|
586
|
+
// In real implementation, use NLP to compare content
|
|
587
|
+
const snippets = results.map(r => r.snippet.toLowerCase());
|
|
588
|
+
const contradictoryTerms = ['not', 'never', 'false', 'incorrect', 'myth'];
|
|
589
|
+
|
|
590
|
+
return snippets.some(snippet =>
|
|
591
|
+
contradictoryTerms.some(term => snippet.includes(term))
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private extractKeyInsights(results: SearchResult[]): string[] {
|
|
596
|
+
// Simplified insight extraction
|
|
597
|
+
const insights: string[] = [];
|
|
598
|
+
const seenTopics = new Set<string>();
|
|
599
|
+
|
|
600
|
+
results.forEach(result => {
|
|
601
|
+
const topic = this.extractMainTopic(result.title);
|
|
602
|
+
if (!seenTopics.has(topic)) {
|
|
603
|
+
seenTopics.add(topic);
|
|
604
|
+
insights.push(`${topic}: ${result.snippet.substring(0, 100)}...`);
|
|
605
|
+
}
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
return insights.slice(0, 5);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
// ============================================================================
|
|
613
|
+
// EXPORT SINGLETON INSTANCE
|
|
614
|
+
// ============================================================================
|
|
615
|
+
|
|
616
|
+
export const searchEngine = new SearchDiscoveryEngine();
|
|
617
|
+
|
|
618
|
+
// ============================================================================
|
|
619
|
+
// EXAMPLE USAGE
|
|
620
|
+
// ============================================================================
|
|
621
|
+
|
|
622
|
+
/*
|
|
623
|
+
// Basic search
|
|
624
|
+
const results = await searchEngine.search('artificial intelligence trends 2024', {
|
|
625
|
+
sources: [SourceType.WEB, SourceType.KNOWLEDGE_BASE],
|
|
626
|
+
maxResults: 10,
|
|
627
|
+
filters: {
|
|
628
|
+
minCredibility: 0.7,
|
|
629
|
+
language: 'en'
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
// Multi-query search with aggregation
|
|
634
|
+
const aggregatedResults = await searchEngine.multiSearch([
|
|
635
|
+
'AI in healthcare',
|
|
636
|
+
'machine learning medical applications',
|
|
637
|
+
'healthcare automation technology'
|
|
638
|
+
], {
|
|
639
|
+
aggregate: true,
|
|
640
|
+
maxResultsPerQuery: 5
|
|
641
|
+
}) as AggregatedResult[];
|
|
642
|
+
|
|
643
|
+
// Add custom knowledge
|
|
644
|
+
searchEngine.addKnowledge('AI Definition', {
|
|
645
|
+
term: 'Artificial Intelligence',
|
|
646
|
+
definition: 'The simulation of human intelligence by machines',
|
|
647
|
+
categories: ['technology', 'computer science']
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
// Listen to events
|
|
651
|
+
searchEngine.on('search_complete', (event) => {
|
|
652
|
+
console.log(`Search completed in ${event.data.processingTime}ms`);
|
|
653
|
+
});
|
|
654
|
+
*/
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": ".",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true,
|
|
13
|
+
"declarationMap": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"moduleResolution": "node"
|
|
17
|
+
},
|
|
18
|
+
"include": [
|
|
19
|
+
"*.ts"
|
|
20
|
+
],
|
|
21
|
+
"exclude": [
|
|
22
|
+
"node_modules",
|
|
23
|
+
"dist"
|
|
24
|
+
]
|
|
25
|
+
}
|