@framers/agentos-ext-web-search 1.1.0 → 1.2.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 +31 -188
- package/dist/index.d.ts +18 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +57 -50
- package/dist/index.js.map +1 -1
- package/dist/services/searchProvider.d.ts +89 -3
- package/dist/services/searchProvider.d.ts.map +1 -1
- package/dist/services/searchProvider.js +342 -26
- package/dist/services/searchProvider.js.map +1 -1
- package/dist/tools/factCheck.d.ts +46 -88
- package/dist/tools/factCheck.d.ts.map +1 -1
- package/dist/tools/factCheck.js +141 -170
- package/dist/tools/factCheck.js.map +1 -1
- package/dist/tools/researchAggregator.d.ts +37 -91
- package/dist/tools/researchAggregator.d.ts.map +1 -1
- package/dist/tools/researchAggregator.js +141 -175
- package/dist/tools/researchAggregator.js.map +1 -1
- package/dist/tools/webSearch.d.ts +31 -59
- package/dist/tools/webSearch.d.ts.map +1 -1
- package/dist/tools/webSearch.js +76 -87
- package/dist/tools/webSearch.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -2
- package/dist/types.js.map +1 -1
- package/manifest.json +14 -9
- package/package.json +26 -16
- package/.eslintrc.json +0 -21
- package/CHANGELOG.md +0 -16
- package/src/index.ts +0 -123
- package/src/services/searchProvider.ts +0 -416
- package/src/tools/factCheck.ts +0 -318
- package/src/tools/researchAggregator.ts +0 -297
- package/src/tools/webSearch.ts +0 -128
- package/src/types.ts +0 -41
- package/test/integration.spec.ts +0 -173
- package/test/searchProvider.spec.ts +0 -153
- package/test/webSearch.spec.ts +0 -128
- package/tsconfig.json +0 -21
- package/vitest.config.ts +0 -32
|
@@ -1,101 +1,47 @@
|
|
|
1
|
-
import { ITool } from '@framers/agentos';
|
|
2
|
-
import { SearchProviderService } from '../services/searchProvider';
|
|
3
1
|
/**
|
|
4
|
-
* Research
|
|
2
|
+
* Research Aggregator Tool — runs multiple searches on a topic and aggregates results.
|
|
5
3
|
*
|
|
6
|
-
*
|
|
7
|
-
* @implements {ITool}
|
|
4
|
+
* Updated to conform to AgentOS `ITool` (inputSchema + ToolExecutionContext).
|
|
8
5
|
*
|
|
9
|
-
* @
|
|
10
|
-
* ```typescript
|
|
11
|
-
* const tool = new ResearchAggregatorTool(searchService);
|
|
12
|
-
* const result = await tool.execute({
|
|
13
|
-
* topic: 'AI safety research',
|
|
14
|
-
* sources: 3,
|
|
15
|
-
* depth: 'comprehensive'
|
|
16
|
-
* });
|
|
17
|
-
* ```
|
|
6
|
+
* @module @framers/agentos-ext-web-search
|
|
18
7
|
*/
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
* @param {string} depth - Research depth level
|
|
54
|
-
* @returns {string[]} Array of search queries
|
|
55
|
-
*/
|
|
8
|
+
import type { ITool, JSONSchemaObject, ToolExecutionContext, ToolExecutionResult } from '@framers/agentos';
|
|
9
|
+
import { SearchProviderService } from '../services/searchProvider.js';
|
|
10
|
+
export interface ResearchAggregatorInput {
|
|
11
|
+
topic: string;
|
|
12
|
+
sources?: number;
|
|
13
|
+
depth?: 'quick' | 'moderate' | 'comprehensive';
|
|
14
|
+
multiSearch?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface ResearchAggregatorOutput {
|
|
17
|
+
topic: string;
|
|
18
|
+
sources: number;
|
|
19
|
+
depth: 'quick' | 'moderate' | 'comprehensive';
|
|
20
|
+
queries: string[];
|
|
21
|
+
aggregatedResults: any;
|
|
22
|
+
timestamp: string;
|
|
23
|
+
}
|
|
24
|
+
export declare class ResearchAggregatorTool implements ITool<ResearchAggregatorInput, ResearchAggregatorOutput> {
|
|
25
|
+
private readonly searchService;
|
|
26
|
+
private readonly defaultMultiSearch;
|
|
27
|
+
readonly id = "research-aggregator-v1";
|
|
28
|
+
/** Tool call name used by the LLM / ToolExecutor. */
|
|
29
|
+
readonly name = "research_aggregate";
|
|
30
|
+
readonly displayName = "Research Aggregator";
|
|
31
|
+
readonly description = "Aggregate research from multiple web searches on a topic (deduplicated + categorized).";
|
|
32
|
+
readonly category = "research";
|
|
33
|
+
readonly hasSideEffects = false;
|
|
34
|
+
readonly inputSchema: JSONSchemaObject;
|
|
35
|
+
readonly requiredCapabilities: string[];
|
|
36
|
+
constructor(searchService: SearchProviderService, defaultMultiSearch?: boolean);
|
|
37
|
+
execute(input: ResearchAggregatorInput, _context: ToolExecutionContext): Promise<ToolExecutionResult<ResearchAggregatorOutput>>;
|
|
38
|
+
validateArgs(input: Record<string, any>): {
|
|
39
|
+
isValid: boolean;
|
|
40
|
+
errors?: any[];
|
|
41
|
+
};
|
|
56
42
|
private generateSearchQueries;
|
|
57
|
-
/**
|
|
58
|
-
* Aggregates and deduplicates search results
|
|
59
|
-
*
|
|
60
|
-
* @private
|
|
61
|
-
* @param {any[]} searchResults - Array of search results from different queries
|
|
62
|
-
* @param {string} topic - The original research topic
|
|
63
|
-
* @returns {Object} Aggregated and organized results
|
|
64
|
-
*/
|
|
65
43
|
private aggregateResults;
|
|
66
|
-
/**
|
|
67
|
-
* Calculates relevance score for a search result
|
|
68
|
-
*
|
|
69
|
-
* @private
|
|
70
|
-
* @param {any} result - Search result object
|
|
71
|
-
* @param {string} topic - Original topic for comparison
|
|
72
|
-
* @returns {number} Relevance score (0-100)
|
|
73
|
-
*/
|
|
74
44
|
private calculateRelevance;
|
|
75
|
-
/**
|
|
76
|
-
* Gets top results across all categories
|
|
77
|
-
*
|
|
78
|
-
* @private
|
|
79
|
-
* @param {any} categories - Categorized results
|
|
80
|
-
* @param {number} count - Number of top results to return
|
|
81
|
-
* @returns {any[]} Top results by relevance
|
|
82
|
-
*/
|
|
83
45
|
private getTopResults;
|
|
84
|
-
/**
|
|
85
|
-
* Validates input parameters
|
|
86
|
-
*
|
|
87
|
-
* @param {any} input - Input to validate
|
|
88
|
-
* @returns {{valid: boolean, errors: string[]}} Validation result
|
|
89
|
-
*/
|
|
90
|
-
validate(input: any): {
|
|
91
|
-
valid: boolean;
|
|
92
|
-
errors: string[];
|
|
93
|
-
};
|
|
94
|
-
/**
|
|
95
|
-
* Returns the JSON schema for the tool's input
|
|
96
|
-
*
|
|
97
|
-
* @returns {Object} JSON Schema object
|
|
98
|
-
*/
|
|
99
|
-
getSchema(): any;
|
|
100
46
|
}
|
|
101
47
|
//# sourceMappingURL=researchAggregator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"researchAggregator.d.ts","sourceRoot":"","sources":["../../src/tools/researchAggregator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"researchAggregator.d.ts","sourceRoot":"","sources":["../../src/tools/researchAggregator.ts"],"names":[],"mappings":"AACA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE3G,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAEtE,MAAM,WAAW,uBAAuB;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,eAAe,CAAC;IAC/C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,eAAe,CAAC;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iBAAiB,EAAE,GAAG,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,sBAAuB,YAAW,KAAK,CAAC,uBAAuB,EAAE,wBAAwB,CAAC;IA2CnG,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IA3CrC,SAAgB,EAAE,4BAA4B;IAC9C,qDAAqD;IACrD,SAAgB,IAAI,wBAAwB;IAC5C,SAAgB,WAAW,yBAAyB;IACpD,SAAgB,WAAW,4FAA4F;IACvH,SAAgB,QAAQ,cAAc;IACtC,SAAgB,cAAc,SAAS;IAEvC,SAAgB,WAAW,EAAE,gBAAgB,CA6B3C;IAEF,SAAgB,oBAAoB,WAA6B;gBAG9C,aAAa,EAAE,qBAAqB,EACpC,kBAAkB,GAAE,OAAe;IAGhD,OAAO,CACX,KAAK,EAAE,uBAAuB,EAC9B,QAAQ,EAAE,oBAAoB,GAC7B,OAAO,CAAC,mBAAmB,CAAC,wBAAwB,CAAC,CAAC;IAkEzD,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAA;KAAE;IAgC9E,OAAO,CAAC,qBAAqB;IA6B7B,OAAO,CAAC,gBAAgB;IAwDxB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,aAAa;CAStB"}
|
|
@@ -1,90 +1,140 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ResearchAggregatorTool = void 0;
|
|
1
|
+
// @ts-nocheck
|
|
4
2
|
/**
|
|
5
|
-
* Research
|
|
3
|
+
* Research Aggregator Tool — runs multiple searches on a topic and aggregates results.
|
|
6
4
|
*
|
|
7
|
-
*
|
|
8
|
-
* @implements {ITool}
|
|
5
|
+
* Updated to conform to AgentOS `ITool` (inputSchema + ToolExecutionContext).
|
|
9
6
|
*
|
|
10
|
-
* @
|
|
11
|
-
* ```typescript
|
|
12
|
-
* const tool = new ResearchAggregatorTool(searchService);
|
|
13
|
-
* const result = await tool.execute({
|
|
14
|
-
* topic: 'AI safety research',
|
|
15
|
-
* sources: 3,
|
|
16
|
-
* depth: 'comprehensive'
|
|
17
|
-
* });
|
|
18
|
-
* ```
|
|
7
|
+
* @module @framers/agentos-ext-web-search
|
|
19
8
|
*/
|
|
20
|
-
class ResearchAggregatorTool {
|
|
9
|
+
export class ResearchAggregatorTool {
|
|
21
10
|
searchService;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
11
|
+
defaultMultiSearch;
|
|
12
|
+
id = 'research-aggregator-v1';
|
|
13
|
+
/** Tool call name used by the LLM / ToolExecutor. */
|
|
14
|
+
name = 'research_aggregate';
|
|
15
|
+
displayName = 'Research Aggregator';
|
|
16
|
+
description = 'Aggregate research from multiple web searches on a topic (deduplicated + categorized).';
|
|
17
|
+
category = 'research';
|
|
18
|
+
hasSideEffects = false;
|
|
19
|
+
inputSchema = {
|
|
20
|
+
type: 'object',
|
|
21
|
+
required: ['topic'],
|
|
22
|
+
properties: {
|
|
23
|
+
topic: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'The research topic to aggregate information about',
|
|
26
|
+
},
|
|
27
|
+
sources: {
|
|
28
|
+
type: 'integer',
|
|
29
|
+
description: 'Number of different search angles to explore',
|
|
30
|
+
default: 3,
|
|
31
|
+
minimum: 1,
|
|
32
|
+
maximum: 10,
|
|
33
|
+
},
|
|
34
|
+
depth: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
description: 'Research depth level',
|
|
37
|
+
enum: ['quick', 'moderate', 'comprehensive'],
|
|
38
|
+
default: 'moderate',
|
|
39
|
+
},
|
|
40
|
+
multiSearch: {
|
|
41
|
+
type: 'boolean',
|
|
42
|
+
description: 'When true, each search query fans out to ALL available providers in parallel, returning merged and deduplicated results ranked by cross-provider agreement.',
|
|
43
|
+
default: false,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
additionalProperties: false,
|
|
47
|
+
};
|
|
48
|
+
requiredCapabilities = ['capability:web_search'];
|
|
49
|
+
constructor(searchService, defaultMultiSearch = false) {
|
|
30
50
|
this.searchService = searchService;
|
|
51
|
+
this.defaultMultiSearch = defaultMultiSearch;
|
|
31
52
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
* @returns {Promise<{success: boolean, output?: any, error?: string}>} Aggregated research results
|
|
40
|
-
*/
|
|
41
|
-
async execute(input) {
|
|
53
|
+
async execute(input, _context) {
|
|
54
|
+
if (!this.searchService) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
error: 'Search service not initialized. Try using browser_navigate to search directly, or set a search API key (SERPER_API_KEY, BRAVE_API_KEY).',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
42
60
|
try {
|
|
43
61
|
const sources = input.sources || 3;
|
|
44
62
|
const depth = input.depth || 'moderate';
|
|
45
|
-
|
|
63
|
+
const useMultiSearch = input.multiSearch ?? this.defaultMultiSearch;
|
|
46
64
|
const queries = this.generateSearchQueries(input.topic, sources, depth);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
const perQueryMaxResults = depth === 'quick' ? 5 : depth === 'moderate' ? 10 : 15;
|
|
66
|
+
let searchResults;
|
|
67
|
+
if (useMultiSearch) {
|
|
68
|
+
// Fan out each query to ALL providers in parallel, then normalize into ProviderResponse shape
|
|
69
|
+
const multiPromises = queries.map((query) => this.searchService
|
|
70
|
+
.multiSearch(query, { maxResults: perQueryMaxResults })
|
|
71
|
+
.then((multi) => ({
|
|
72
|
+
provider: 'multi',
|
|
73
|
+
results: multi.results,
|
|
74
|
+
metadata: { query, timestamp: multi.metadata.timestamp },
|
|
75
|
+
}))
|
|
76
|
+
.catch((err) => ({
|
|
77
|
+
provider: 'error',
|
|
78
|
+
results: [],
|
|
79
|
+
metadata: { query, error: err?.message || String(err), timestamp: new Date().toISOString() },
|
|
80
|
+
})));
|
|
81
|
+
searchResults = (await Promise.all(multiPromises));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const searchPromises = queries.map((query) => this.searchService
|
|
85
|
+
.search(query, { maxResults: perQueryMaxResults })
|
|
86
|
+
.catch((err) => ({
|
|
87
|
+
provider: 'error',
|
|
88
|
+
results: [],
|
|
89
|
+
metadata: { query, error: err?.message || String(err), timestamp: new Date().toISOString() },
|
|
90
|
+
})));
|
|
91
|
+
searchResults = (await Promise.all(searchPromises));
|
|
92
|
+
}
|
|
93
|
+
const aggregatedResults = this.aggregateResults(searchResults, input.topic);
|
|
58
94
|
return {
|
|
59
95
|
success: true,
|
|
60
96
|
output: {
|
|
61
97
|
topic: input.topic,
|
|
62
|
-
sources
|
|
63
|
-
depth
|
|
64
|
-
queries
|
|
65
|
-
aggregatedResults
|
|
66
|
-
timestamp: new Date().toISOString()
|
|
67
|
-
}
|
|
98
|
+
sources,
|
|
99
|
+
depth,
|
|
100
|
+
queries,
|
|
101
|
+
aggregatedResults,
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
},
|
|
68
104
|
};
|
|
69
105
|
}
|
|
70
106
|
catch (error) {
|
|
71
|
-
return {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
107
|
+
return { success: false, error: error?.message || String(error) };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
validateArgs(input) {
|
|
111
|
+
const errors = [];
|
|
112
|
+
if (!input.topic) {
|
|
113
|
+
errors.push('Topic is required');
|
|
114
|
+
}
|
|
115
|
+
else if (typeof input.topic !== 'string') {
|
|
116
|
+
errors.push('Topic must be a string');
|
|
117
|
+
}
|
|
118
|
+
if (input.sources !== undefined) {
|
|
119
|
+
if (typeof input.sources !== 'number' || input.sources < 1 || input.sources > 10) {
|
|
120
|
+
errors.push('Sources must be a number between 1 and 10');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (input.depth !== undefined) {
|
|
124
|
+
if (!['quick', 'moderate', 'comprehensive'].includes(input.depth)) {
|
|
125
|
+
errors.push('Depth must be quick, moderate, or comprehensive');
|
|
126
|
+
}
|
|
75
127
|
}
|
|
128
|
+
if (input.multiSearch !== undefined && typeof input.multiSearch !== 'boolean') {
|
|
129
|
+
errors.push('multiSearch must be a boolean');
|
|
130
|
+
}
|
|
131
|
+
return errors.length === 0 ? { isValid: true } : { isValid: false, errors };
|
|
76
132
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
* @private
|
|
81
|
-
* @param {string} topic - The research topic
|
|
82
|
-
* @param {number} count - Number of queries to generate
|
|
83
|
-
* @param {string} depth - Research depth level
|
|
84
|
-
* @returns {string[]} Array of search queries
|
|
85
|
-
*/
|
|
133
|
+
// ============================================================================
|
|
134
|
+
// Internals (ported from the previous implementation)
|
|
135
|
+
// ============================================================================
|
|
86
136
|
generateSearchQueries(topic, count, depth) {
|
|
87
|
-
const queries = [topic];
|
|
137
|
+
const queries = [topic];
|
|
88
138
|
const modifiers = {
|
|
89
139
|
quick: ['overview', 'summary'],
|
|
90
140
|
moderate: ['overview', 'recent developments', 'key concepts', 'applications'],
|
|
@@ -96,8 +146,8 @@ class ResearchAggregatorTool {
|
|
|
96
146
|
'future directions',
|
|
97
147
|
'industry applications',
|
|
98
148
|
'academic papers',
|
|
99
|
-
'expert opinions'
|
|
100
|
-
]
|
|
149
|
+
'expert opinions',
|
|
150
|
+
],
|
|
101
151
|
};
|
|
102
152
|
const selectedModifiers = modifiers[depth];
|
|
103
153
|
for (let i = 1; i < Math.min(count, selectedModifiers.length + 1); i++) {
|
|
@@ -107,42 +157,33 @@ class ResearchAggregatorTool {
|
|
|
107
157
|
}
|
|
108
158
|
return queries;
|
|
109
159
|
}
|
|
110
|
-
/**
|
|
111
|
-
* Aggregates and deduplicates search results
|
|
112
|
-
*
|
|
113
|
-
* @private
|
|
114
|
-
* @param {any[]} searchResults - Array of search results from different queries
|
|
115
|
-
* @param {string} topic - The original research topic
|
|
116
|
-
* @returns {Object} Aggregated and organized results
|
|
117
|
-
*/
|
|
118
160
|
aggregateResults(searchResults, topic) {
|
|
119
161
|
const seenUrls = new Set();
|
|
120
162
|
const categories = {
|
|
121
163
|
overview: [],
|
|
122
164
|
research: [],
|
|
123
165
|
applications: [],
|
|
124
|
-
resources: []
|
|
166
|
+
resources: [],
|
|
125
167
|
};
|
|
126
168
|
for (const searchResult of searchResults) {
|
|
127
169
|
if (!searchResult.results)
|
|
128
170
|
continue;
|
|
129
171
|
for (const result of searchResult.results) {
|
|
130
|
-
// Skip duplicates
|
|
131
172
|
if (seenUrls.has(result.url))
|
|
132
173
|
continue;
|
|
133
174
|
seenUrls.add(result.url);
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const lowerSnippet = result.snippet.toLowerCase();
|
|
137
|
-
if (lowerTitle.includes('overview') || lowerTitle.includes('introduction') ||
|
|
138
|
-
lowerTitle.includes('what is')) {
|
|
175
|
+
const lowerTitle = String(result.title || '').toLowerCase();
|
|
176
|
+
if (lowerTitle.includes('overview') || lowerTitle.includes('introduction') || lowerTitle.includes('what is')) {
|
|
139
177
|
categories.overview.push(result);
|
|
140
178
|
}
|
|
141
|
-
else if (lowerTitle.includes('research') ||
|
|
142
|
-
lowerTitle.includes('
|
|
179
|
+
else if (lowerTitle.includes('research') ||
|
|
180
|
+
lowerTitle.includes('study') ||
|
|
181
|
+
lowerTitle.includes('paper') ||
|
|
182
|
+
lowerTitle.includes('journal')) {
|
|
143
183
|
categories.research.push(result);
|
|
144
184
|
}
|
|
145
|
-
else if (lowerTitle.includes('application') ||
|
|
185
|
+
else if (lowerTitle.includes('application') ||
|
|
186
|
+
lowerTitle.includes('use case') ||
|
|
146
187
|
lowerTitle.includes('implementation')) {
|
|
147
188
|
categories.applications.push(result);
|
|
148
189
|
}
|
|
@@ -151,125 +192,50 @@ class ResearchAggregatorTool {
|
|
|
151
192
|
}
|
|
152
193
|
}
|
|
153
194
|
}
|
|
154
|
-
// Calculate relevance scores
|
|
155
195
|
const scoredCategories = Object.entries(categories).reduce((acc, [key, items]) => {
|
|
156
|
-
acc[key] = items
|
|
196
|
+
acc[key] = items
|
|
197
|
+
.map((item) => ({
|
|
157
198
|
...item,
|
|
158
|
-
relevanceScore: this.calculateRelevance(item, topic)
|
|
159
|
-
}))
|
|
199
|
+
relevanceScore: this.calculateRelevance(item, topic),
|
|
200
|
+
}))
|
|
201
|
+
.sort((a, b) => b.relevanceScore - a.relevanceScore);
|
|
160
202
|
return acc;
|
|
161
203
|
}, {});
|
|
162
204
|
return {
|
|
163
205
|
categories: scoredCategories,
|
|
164
206
|
totalResults: seenUrls.size,
|
|
165
|
-
topResults: this.getTopResults(scoredCategories, 10)
|
|
207
|
+
topResults: this.getTopResults(scoredCategories, 10),
|
|
166
208
|
};
|
|
167
209
|
}
|
|
168
|
-
/**
|
|
169
|
-
* Calculates relevance score for a search result
|
|
170
|
-
*
|
|
171
|
-
* @private
|
|
172
|
-
* @param {any} result - Search result object
|
|
173
|
-
* @param {string} topic - Original topic for comparison
|
|
174
|
-
* @returns {number} Relevance score (0-100)
|
|
175
|
-
*/
|
|
176
210
|
calculateRelevance(result, topic) {
|
|
177
|
-
let score = 50;
|
|
211
|
+
let score = 50;
|
|
178
212
|
const topicWords = topic.toLowerCase().split(/\s+/);
|
|
179
|
-
const titleWords = result.title.toLowerCase();
|
|
180
|
-
const snippetWords = result.snippet.toLowerCase();
|
|
181
|
-
// Check title relevance
|
|
213
|
+
const titleWords = String(result.title || '').toLowerCase();
|
|
214
|
+
const snippetWords = String(result.snippet || '').toLowerCase();
|
|
182
215
|
for (const word of topicWords) {
|
|
183
216
|
if (titleWords.includes(word))
|
|
184
217
|
score += 15;
|
|
185
218
|
if (snippetWords.includes(word))
|
|
186
219
|
score += 5;
|
|
187
220
|
}
|
|
188
|
-
// Boost for certain indicators
|
|
189
221
|
if (titleWords.includes('official'))
|
|
190
222
|
score += 10;
|
|
191
223
|
if (titleWords.includes('guide'))
|
|
192
224
|
score += 5;
|
|
193
225
|
if (titleWords.includes('tutorial'))
|
|
194
226
|
score += 5;
|
|
227
|
+
// Bonus for multi-search results with cross-provider agreement
|
|
228
|
+
if (typeof result.confidenceScore === 'number') {
|
|
229
|
+
score += (result.confidenceScore / 100) * 20;
|
|
230
|
+
}
|
|
195
231
|
return Math.min(100, score);
|
|
196
232
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Gets top results across all categories
|
|
199
|
-
*
|
|
200
|
-
* @private
|
|
201
|
-
* @param {any} categories - Categorized results
|
|
202
|
-
* @param {number} count - Number of top results to return
|
|
203
|
-
* @returns {any[]} Top results by relevance
|
|
204
|
-
*/
|
|
205
233
|
getTopResults(categories, count) {
|
|
206
234
|
const allResults = [];
|
|
207
235
|
for (const items of Object.values(categories)) {
|
|
208
236
|
allResults.push(...items);
|
|
209
237
|
}
|
|
210
|
-
return allResults
|
|
211
|
-
.sort((a, b) => b.relevanceScore - a.relevanceScore)
|
|
212
|
-
.slice(0, count);
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Validates input parameters
|
|
216
|
-
*
|
|
217
|
-
* @param {any} input - Input to validate
|
|
218
|
-
* @returns {{valid: boolean, errors: string[]}} Validation result
|
|
219
|
-
*/
|
|
220
|
-
validate(input) {
|
|
221
|
-
const errors = [];
|
|
222
|
-
if (!input.topic) {
|
|
223
|
-
errors.push('Topic is required');
|
|
224
|
-
}
|
|
225
|
-
else if (typeof input.topic !== 'string') {
|
|
226
|
-
errors.push('Topic must be a string');
|
|
227
|
-
}
|
|
228
|
-
if (input.sources !== undefined) {
|
|
229
|
-
if (typeof input.sources !== 'number' || input.sources < 1 || input.sources > 10) {
|
|
230
|
-
errors.push('Sources must be a number between 1 and 10');
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (input.depth !== undefined) {
|
|
234
|
-
if (!['quick', 'moderate', 'comprehensive'].includes(input.depth)) {
|
|
235
|
-
errors.push('Depth must be quick, moderate, or comprehensive');
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
return {
|
|
239
|
-
valid: errors.length === 0,
|
|
240
|
-
errors
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Returns the JSON schema for the tool's input
|
|
245
|
-
*
|
|
246
|
-
* @returns {Object} JSON Schema object
|
|
247
|
-
*/
|
|
248
|
-
getSchema() {
|
|
249
|
-
return {
|
|
250
|
-
type: 'object',
|
|
251
|
-
required: ['topic'],
|
|
252
|
-
properties: {
|
|
253
|
-
topic: {
|
|
254
|
-
type: 'string',
|
|
255
|
-
description: 'The research topic to aggregate information about'
|
|
256
|
-
},
|
|
257
|
-
sources: {
|
|
258
|
-
type: 'number',
|
|
259
|
-
description: 'Number of different search angles to explore',
|
|
260
|
-
default: 3,
|
|
261
|
-
minimum: 1,
|
|
262
|
-
maximum: 10
|
|
263
|
-
},
|
|
264
|
-
depth: {
|
|
265
|
-
type: 'string',
|
|
266
|
-
description: 'Research depth level',
|
|
267
|
-
enum: ['quick', 'moderate', 'comprehensive'],
|
|
268
|
-
default: 'moderate'
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
};
|
|
238
|
+
return allResults.sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, count);
|
|
272
239
|
}
|
|
273
240
|
}
|
|
274
|
-
exports.ResearchAggregatorTool = ResearchAggregatorTool;
|
|
275
241
|
//# sourceMappingURL=researchAggregator.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"researchAggregator.js","sourceRoot":"","sources":["../../src/tools/researchAggregator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"researchAggregator.js","sourceRoot":"","sources":["../../src/tools/researchAggregator.ts"],"names":[],"mappings":"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"}
|