@elizaos/plugin-web-search 1.0.1 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/index.d.ts +5 -65
- package/dist/index.js +162 -465
- package/dist/index.js.map +1 -1
- package/package.json +11 -21
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ This plugin provides functionality to:
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
|
|
18
|
+
bun install @elizaos/plugin-web-search
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
## Configuration
|
|
@@ -97,19 +97,19 @@ const response = MaxTokens(searchResult, DEFAULT_MAX_WEB_SEARCH_TOKENS);
|
|
|
97
97
|
### Building
|
|
98
98
|
|
|
99
99
|
```bash
|
|
100
|
-
|
|
100
|
+
bun run build
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
### Testing
|
|
104
104
|
|
|
105
105
|
```bash
|
|
106
|
-
|
|
106
|
+
bun run test
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
### Development Mode
|
|
110
110
|
|
|
111
111
|
```bash
|
|
112
|
-
|
|
112
|
+
bun run dev
|
|
113
113
|
```
|
|
114
114
|
|
|
115
115
|
## Dependencies
|
package/dist/index.d.ts
CHANGED
|
@@ -1,67 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Service, IAgentRuntime } from '@elizaos/core';
|
|
1
|
+
import { SearchCategoryRegistration, Plugin, IAgentRuntime } from '@elizaos/core';
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
type SearchResult = {
|
|
8
|
-
title: string;
|
|
9
|
-
url: string;
|
|
10
|
-
content: string;
|
|
11
|
-
rawContent?: string;
|
|
12
|
-
score: number;
|
|
13
|
-
publishedDate?: string;
|
|
14
|
-
};
|
|
15
|
-
type SearchImage = {
|
|
16
|
-
url: string;
|
|
17
|
-
description?: string;
|
|
18
|
-
};
|
|
19
|
-
type SearchResponse = {
|
|
20
|
-
answer?: string;
|
|
21
|
-
query: string;
|
|
22
|
-
responseTime: number;
|
|
23
|
-
images: SearchImage[];
|
|
24
|
-
results: SearchResult[];
|
|
25
|
-
};
|
|
26
|
-
interface SearchOptions {
|
|
27
|
-
auto_parameters?: boolean;
|
|
28
|
-
topic?: "general" | "news" | "finance";
|
|
29
|
-
search_depth?: "basic" | "advanced";
|
|
30
|
-
chunks_per_source?: number;
|
|
31
|
-
max_results?: number;
|
|
32
|
-
time_range?: "day" | "week" | "month" | "year" | "d" | "w" | "m" | "y";
|
|
33
|
-
start_date?: string;
|
|
34
|
-
end_date?: string;
|
|
35
|
-
include_answer?: boolean | "basic" | "advanced";
|
|
36
|
-
include_raw_content?: boolean | "markdown" | "text";
|
|
37
|
-
include_images?: boolean;
|
|
38
|
-
include_image_descriptions?: boolean;
|
|
39
|
-
include_favicon?: boolean;
|
|
40
|
-
include_domains?: string[];
|
|
41
|
-
exclude_domains?: string[];
|
|
42
|
-
country?: string;
|
|
43
|
-
}
|
|
3
|
+
declare const WEB_SEARCH_CATEGORY: SearchCategoryRegistration;
|
|
4
|
+
declare function registerWebSearchCategory(runtime: IAgentRuntime): void;
|
|
5
|
+
declare const webSearchPlugin: Plugin;
|
|
44
6
|
|
|
45
|
-
|
|
46
|
-
static serviceType: "TAVILY";
|
|
47
|
-
private tavilyClient;
|
|
48
|
-
constructor(runtime: IAgentRuntime);
|
|
49
|
-
static start(runtime: IAgentRuntime): Promise<TavilyService>;
|
|
50
|
-
initialize(runtime: IAgentRuntime): Promise<void>;
|
|
51
|
-
get capabilityDescription(): string;
|
|
52
|
-
stop(): Promise<void>;
|
|
53
|
-
search(query: string, options?: SearchOptions): Promise<SearchResponse>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
declare const webSearchPlugin: {
|
|
57
|
-
name: string;
|
|
58
|
-
description: string;
|
|
59
|
-
actions: _elizaos_core.Action[];
|
|
60
|
-
evaluators: any[];
|
|
61
|
-
providers: any[];
|
|
62
|
-
services: (typeof TavilyService)[];
|
|
63
|
-
clients: any[];
|
|
64
|
-
adapters: any[];
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
export { webSearchPlugin as default, webSearchPlugin };
|
|
7
|
+
export { WEB_SEARCH_CATEGORY, webSearchPlugin as default, registerWebSearchCategory, webSearchPlugin };
|
package/dist/index.js
CHANGED
|
@@ -1,500 +1,197 @@
|
|
|
1
|
-
// src/
|
|
2
|
-
import {
|
|
3
|
-
logger,
|
|
4
|
-
ModelType,
|
|
5
|
-
composePromptFromState,
|
|
6
|
-
parseKeyValueXml
|
|
7
|
-
} from "@elizaos/core";
|
|
8
|
-
var DEFAULT_MAX_WEB_SEARCH_CHARS = 16e3;
|
|
9
|
-
function buildExtractionTemplate(conversationContext) {
|
|
10
|
-
return `# Web Search Parameter Extraction
|
|
11
|
-
|
|
12
|
-
## Recent Conversation
|
|
13
|
-
${conversationContext}
|
|
14
|
-
|
|
15
|
-
## Task
|
|
16
|
-
Extract the search query and parameters from the conversation above. The user wants to search the web for information.
|
|
17
|
-
|
|
18
|
-
## Instructions
|
|
19
|
-
- Determine the exact search query the user wants
|
|
20
|
-
- If the topic is about crypto, DeFi, finance, or markets, set topic to "finance"
|
|
21
|
-
- Only include optional parameters if clearly indicated by the user
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { ServiceType as ServiceType2 } from "@elizaos/core";
|
|
22
3
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<query>the exact query to search</query>
|
|
31
|
-
<topic>general or finance</topic>
|
|
32
|
-
</response>`;
|
|
4
|
+
// src/services/webSearchService.ts
|
|
5
|
+
import { IWebSearchService, logger, ServiceType } from "@elizaos/core";
|
|
6
|
+
import { tavily } from "@tavily/core";
|
|
7
|
+
function parsePublishedDate(value) {
|
|
8
|
+
if (!value) return void 0;
|
|
9
|
+
const date = new Date(value);
|
|
10
|
+
return Number.isNaN(date.getTime()) ? void 0 : date;
|
|
33
11
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
12
|
+
function normalizeResponse(query, response) {
|
|
13
|
+
const results = (response.results ?? []).map((result) => {
|
|
14
|
+
const content = result.content ?? "";
|
|
15
|
+
return {
|
|
16
|
+
title: result.title ?? "Untitled",
|
|
17
|
+
url: result.url ?? "",
|
|
18
|
+
description: content,
|
|
19
|
+
content,
|
|
20
|
+
rawContent: result.rawContent,
|
|
21
|
+
score: typeof result.score === "number" ? result.score : 0,
|
|
22
|
+
publishedDate: parsePublishedDate(result.publishedDate)
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
const images = (response.images ?? []).map(
|
|
26
|
+
(image) => typeof image === "string" ? { url: image } : { url: image.url ?? "", description: image.description }
|
|
27
|
+
).filter((image) => image.url);
|
|
28
|
+
return {
|
|
29
|
+
answer: response.answer,
|
|
30
|
+
query: response.query ?? query,
|
|
31
|
+
responseTime: response.responseTime,
|
|
32
|
+
images,
|
|
33
|
+
results
|
|
34
|
+
};
|
|
36
35
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
{ src: "webSearch:extractParams", source: "actionParams" },
|
|
48
|
-
"Using params from state"
|
|
49
|
-
);
|
|
50
|
-
return stateParams;
|
|
36
|
+
function freshnessToDays(freshness) {
|
|
37
|
+
switch (freshness) {
|
|
38
|
+
case "day":
|
|
39
|
+
return 1;
|
|
40
|
+
case "week":
|
|
41
|
+
return 7;
|
|
42
|
+
case "month":
|
|
43
|
+
return 30;
|
|
44
|
+
default:
|
|
45
|
+
return 3;
|
|
51
46
|
}
|
|
52
|
-
logger.info(
|
|
53
|
-
{ src: "webSearch:extractParams", source: "llm" },
|
|
54
|
-
"Extracting params via LLM"
|
|
55
|
-
);
|
|
56
|
-
try {
|
|
57
|
-
const extractionState = await runtime.composeState(
|
|
58
|
-
message,
|
|
59
|
-
["RECENT_MESSAGES"],
|
|
60
|
-
true
|
|
61
|
-
);
|
|
62
|
-
const conversationLog = (_e = extractionState == null ? void 0 : extractionState.values) == null ? void 0 : _e.conversationLog;
|
|
63
|
-
const recentMessages = (_f = extractionState == null ? void 0 : extractionState.values) == null ? void 0 : _f.recentMessages;
|
|
64
|
-
const conversationContext = typeof conversationLog === "string" && conversationLog.trim() || typeof recentMessages === "string" && recentMessages.trim() || "";
|
|
65
|
-
const contextSource = typeof conversationLog === "string" && conversationLog.trim() ? "conversationLog" : "recentMessages";
|
|
66
|
-
logger.debug(
|
|
67
|
-
{ src: "webSearch:extractParams", contextSource },
|
|
68
|
-
"Using conversation context"
|
|
69
|
-
);
|
|
70
|
-
const template = buildExtractionTemplate(conversationContext);
|
|
71
|
-
const prompt = composePromptFromState({
|
|
72
|
-
state: extractionState,
|
|
73
|
-
template
|
|
74
|
-
});
|
|
75
|
-
const response = await runtime.useModel(ModelType.TEXT_SMALL, { prompt });
|
|
76
|
-
const parsed = parseKeyValueXml(response || "");
|
|
77
|
-
if (parsed == null ? void 0 : parsed.query) {
|
|
78
|
-
const extractedParams = {
|
|
79
|
-
query: String(parsed.query).trim(),
|
|
80
|
-
topic: parsed.topic === "finance" ? "finance" : "general"
|
|
81
|
-
};
|
|
82
|
-
logger.info(
|
|
83
|
-
{ src: "webSearch:extractParams", query: extractedParams.query },
|
|
84
|
-
"Extracted query via LLM"
|
|
85
|
-
);
|
|
86
|
-
return extractedParams;
|
|
87
|
-
}
|
|
88
|
-
} catch (err) {
|
|
89
|
-
logger.warn(
|
|
90
|
-
{ src: "webSearch:extractParams", error: err.message },
|
|
91
|
-
"LLM extraction failed, falling back to message text"
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
const messageText = (_h = (_g = message.content) == null ? void 0 : _g.text) == null ? void 0 : _h.trim();
|
|
95
|
-
if (messageText) {
|
|
96
|
-
logger.info(
|
|
97
|
-
{ src: "webSearch:extractParams", source: "messageText" },
|
|
98
|
-
"Using message text as query"
|
|
99
|
-
);
|
|
100
|
-
return { query: messageText };
|
|
101
|
-
}
|
|
102
|
-
return {};
|
|
103
47
|
}
|
|
104
|
-
var
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
"SEARCH_WEB",
|
|
108
|
-
"INTERNET_SEARCH",
|
|
109
|
-
"LOOKUP",
|
|
110
|
-
"QUERY_WEB",
|
|
111
|
-
"FIND_ONLINE",
|
|
112
|
-
"SEARCH_ENGINE",
|
|
113
|
-
"WEB_LOOKUP",
|
|
114
|
-
"ONLINE_SEARCH",
|
|
115
|
-
"FIND_INFORMATION"
|
|
116
|
-
],
|
|
117
|
-
suppressInitialMessage: true,
|
|
118
|
-
description: "Search the web using Tavily. Supports general web search and finance topics (crypto/DeFi/markets). Use when other actions/providers can't provide accurate or current info.\n\nIMPORTANT - Result Quality Check:\n- If search returns off-topic or poor results, RETRY with parameter adjustments in the SAME round\n- Try: topic='finance' for crypto/markets, source filter (theblock.com, coindesk.com), broader time_range, advanced search_depth, or rephrased query\n- For crypto/DeFi content: use topic='finance' + source from [theblock.com, coindesk.com, decrypt.co, dlnews.com]\n- Don't give up after one attempt if results are clearly irrelevant",
|
|
119
|
-
// Parameter schema for tool calling
|
|
120
|
-
parameters: {
|
|
121
|
-
query: {
|
|
122
|
-
type: "string",
|
|
123
|
-
description: "The search query to look up on the web",
|
|
124
|
-
required: true
|
|
125
|
-
},
|
|
126
|
-
topic: {
|
|
127
|
-
type: "string",
|
|
128
|
-
description: "Search topic: 'general' for web search, 'finance' for financial/crypto/DeFi content. Defaults to 'general'.",
|
|
129
|
-
required: false
|
|
130
|
-
},
|
|
131
|
-
source: {
|
|
132
|
-
type: "string",
|
|
133
|
-
description: "Specific source domain to limit results (e.g., 'bloomberg.com', 'reuters.com'). Uses site: operator.",
|
|
134
|
-
required: false
|
|
135
|
-
},
|
|
136
|
-
max_results: {
|
|
137
|
-
type: "number",
|
|
138
|
-
description: "Maximum number of results to return (1-20). Defaults to 5.",
|
|
139
|
-
required: false
|
|
140
|
-
},
|
|
141
|
-
search_depth: {
|
|
142
|
-
type: "string",
|
|
143
|
-
description: "Search depth: 'basic' for quick results or 'advanced' for comprehensive search. Defaults to 'basic'.",
|
|
144
|
-
required: false
|
|
145
|
-
},
|
|
146
|
-
time_range: {
|
|
147
|
-
type: "string",
|
|
148
|
-
description: "Time range filter: 'day', 'week', 'month', 'year' (or 'd', 'w', 'm', 'y')",
|
|
149
|
-
required: false
|
|
150
|
-
},
|
|
151
|
-
start_date: {
|
|
152
|
-
type: "string",
|
|
153
|
-
description: "Start date filter in YYYY-MM-DD format (returns results after this date)",
|
|
154
|
-
required: false
|
|
155
|
-
},
|
|
156
|
-
end_date: {
|
|
157
|
-
type: "string",
|
|
158
|
-
description: "End date filter in YYYY-MM-DD format (returns results before this date)",
|
|
159
|
-
required: false
|
|
160
|
-
}
|
|
161
|
-
},
|
|
162
|
-
validate: async (runtime, _message, _state) => {
|
|
163
|
-
try {
|
|
164
|
-
const service = runtime.getService("TAVILY");
|
|
165
|
-
return !!service;
|
|
166
|
-
} catch (err) {
|
|
167
|
-
logger.warn(
|
|
168
|
-
{ src: "webSearch:validate", error: err.message },
|
|
169
|
-
"TavilyService not available"
|
|
170
|
-
);
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
handler: async (runtime, message, state, _options, callback) => {
|
|
175
|
-
var _a, _b;
|
|
176
|
-
try {
|
|
177
|
-
const tavilyService = runtime.getService("TAVILY");
|
|
178
|
-
if (!tavilyService) {
|
|
179
|
-
throw new Error("TavilyService not initialized");
|
|
180
|
-
}
|
|
181
|
-
const params = await extractSearchParams(runtime, message, state);
|
|
182
|
-
const query = (_a = params == null ? void 0 : params.query) == null ? void 0 : _a.trim();
|
|
183
|
-
if (!query) {
|
|
184
|
-
const errorMsg = "Missing required parameter 'query'. Please specify what to search for.";
|
|
185
|
-
logger.error({ src: "webSearch:handler" }, errorMsg);
|
|
186
|
-
const emptyResult = {
|
|
187
|
-
text: errorMsg,
|
|
188
|
-
success: false,
|
|
189
|
-
data: {
|
|
190
|
-
actionName: "WEB_SEARCH"
|
|
191
|
-
},
|
|
192
|
-
error: "missing_required_parameter"
|
|
193
|
-
};
|
|
194
|
-
if (callback) {
|
|
195
|
-
callback({
|
|
196
|
-
text: emptyResult.text,
|
|
197
|
-
content: { error: "missing_required_parameter", details: errorMsg }
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
return emptyResult;
|
|
201
|
-
}
|
|
202
|
-
const source = (_b = params == null ? void 0 : params.source) == null ? void 0 : _b.trim();
|
|
203
|
-
const topic = (params == null ? void 0 : params.topic) === "finance" ? "finance" : "general";
|
|
204
|
-
const maxResults = (params == null ? void 0 : params.max_results) ? Math.min(Math.max(1, params.max_results), 20) : 5;
|
|
205
|
-
const searchDepth = (params == null ? void 0 : params.search_depth) === "advanced" ? "advanced" : "basic";
|
|
206
|
-
let enhancedQuery = query;
|
|
207
|
-
if (source) {
|
|
208
|
-
enhancedQuery = `${query} site:${source}`;
|
|
209
|
-
logger.info(
|
|
210
|
-
{ src: "webSearch:handler", source },
|
|
211
|
-
"Searching with source filter"
|
|
212
|
-
);
|
|
213
|
-
}
|
|
214
|
-
logger.info(
|
|
215
|
-
{ src: "webSearch:handler", query: enhancedQuery, topic },
|
|
216
|
-
"Executing web search"
|
|
217
|
-
);
|
|
218
|
-
const inputParams = {
|
|
219
|
-
query,
|
|
220
|
-
topic,
|
|
221
|
-
source,
|
|
222
|
-
max_results: maxResults,
|
|
223
|
-
search_depth: searchDepth,
|
|
224
|
-
time_range: params == null ? void 0 : params.time_range,
|
|
225
|
-
start_date: params == null ? void 0 : params.start_date,
|
|
226
|
-
end_date: params == null ? void 0 : params.end_date
|
|
227
|
-
};
|
|
228
|
-
const searchResponse = await tavilyService.search(enhancedQuery, {
|
|
229
|
-
topic,
|
|
230
|
-
max_results: maxResults,
|
|
231
|
-
search_depth: searchDepth,
|
|
232
|
-
time_range: params == null ? void 0 : params.time_range,
|
|
233
|
-
start_date: params == null ? void 0 : params.start_date,
|
|
234
|
-
end_date: params == null ? void 0 : params.end_date,
|
|
235
|
-
include_answer: true,
|
|
236
|
-
include_images: false
|
|
237
|
-
});
|
|
238
|
-
if (searchResponse && searchResponse.results.length) {
|
|
239
|
-
const responseList = searchResponse.answer ? `${searchResponse.answer}${Array.isArray(searchResponse.results) && searchResponse.results.length > 0 ? `
|
|
240
|
-
|
|
241
|
-
For more details, you can check out these resources:
|
|
242
|
-
${searchResponse.results.map(
|
|
243
|
-
(result2, index) => `${index + 1}. [${result2.title}](${result2.url})`
|
|
244
|
-
).join("\n")}` : ""}` : "";
|
|
245
|
-
const result = {
|
|
246
|
-
text: MaxTokens(responseList, DEFAULT_MAX_WEB_SEARCH_CHARS),
|
|
247
|
-
success: true,
|
|
248
|
-
data: {
|
|
249
|
-
...searchResponse,
|
|
250
|
-
actionName: "WEB_SEARCH"
|
|
251
|
-
},
|
|
252
|
-
input: inputParams
|
|
253
|
-
};
|
|
254
|
-
if (callback) {
|
|
255
|
-
callback({
|
|
256
|
-
text: result.text,
|
|
257
|
-
actions: ["WEB_SEARCH"],
|
|
258
|
-
data: result.data
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
return result;
|
|
262
|
-
}
|
|
263
|
-
const noResult = {
|
|
264
|
-
text: "I couldn't find relevant results for that query.",
|
|
265
|
-
success: false,
|
|
266
|
-
data: {
|
|
267
|
-
actionName: "WEB_SEARCH"
|
|
268
|
-
},
|
|
269
|
-
input: inputParams
|
|
270
|
-
};
|
|
271
|
-
if (callback) {
|
|
272
|
-
callback({ text: noResult.text });
|
|
273
|
-
}
|
|
274
|
-
return noResult;
|
|
275
|
-
} catch (error) {
|
|
276
|
-
const errMsg = error instanceof Error ? error.message : String(error);
|
|
277
|
-
logger.error(
|
|
278
|
-
{ src: "webSearch:handler", error: errMsg },
|
|
279
|
-
"Action failed"
|
|
280
|
-
);
|
|
281
|
-
const errorResult = {
|
|
282
|
-
text: `Web search failed: ${errMsg}`,
|
|
283
|
-
success: false,
|
|
284
|
-
data: {
|
|
285
|
-
actionName: "WEB_SEARCH"
|
|
286
|
-
},
|
|
287
|
-
error: errMsg
|
|
288
|
-
};
|
|
289
|
-
if (callback) {
|
|
290
|
-
callback({
|
|
291
|
-
text: errorResult.text,
|
|
292
|
-
content: { error: "web_search_failed", details: errMsg }
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
return errorResult;
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
|
-
examples: [
|
|
299
|
-
[
|
|
300
|
-
{
|
|
301
|
-
name: "{{user}}",
|
|
302
|
-
content: {
|
|
303
|
-
text: "Latest Aave news"
|
|
304
|
-
}
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
name: "{{agent}}",
|
|
308
|
-
content: {
|
|
309
|
-
text: "Let me search for Aave news from crypto sources:",
|
|
310
|
-
action: "WEB_SEARCH",
|
|
311
|
-
actionParams: {
|
|
312
|
-
query: "Aave protocol",
|
|
313
|
-
topic: "finance",
|
|
314
|
-
source: "theblock.com",
|
|
315
|
-
time_range: "week"
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
],
|
|
320
|
-
[
|
|
321
|
-
{
|
|
322
|
-
name: "{{user}}",
|
|
323
|
-
content: {
|
|
324
|
-
text: "Find the latest news about SpaceX launches."
|
|
325
|
-
}
|
|
326
|
-
},
|
|
327
|
-
{
|
|
328
|
-
name: "{{agent}}",
|
|
329
|
-
content: {
|
|
330
|
-
text: "Here is the latest news about SpaceX launches:",
|
|
331
|
-
action: "WEB_SEARCH"
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
],
|
|
335
|
-
[
|
|
336
|
-
{
|
|
337
|
-
name: "{{user}}",
|
|
338
|
-
content: {
|
|
339
|
-
text: "Can you find details about the iPhone 16 release?"
|
|
340
|
-
}
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
name: "{{agent}}",
|
|
344
|
-
content: {
|
|
345
|
-
text: "Here are the details I found about the iPhone 16 release:",
|
|
346
|
-
action: "WEB_SEARCH"
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
],
|
|
350
|
-
[
|
|
351
|
-
{
|
|
352
|
-
name: "{{user}}",
|
|
353
|
-
content: {
|
|
354
|
-
text: "What is the schedule for the next FIFA World Cup?"
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
{
|
|
358
|
-
name: "{{agent}}",
|
|
359
|
-
content: {
|
|
360
|
-
text: "Here is the schedule for the next FIFA World Cup:",
|
|
361
|
-
action: "WEB_SEARCH"
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
],
|
|
365
|
-
[
|
|
366
|
-
{
|
|
367
|
-
name: "{{user}}",
|
|
368
|
-
content: { text: "Check the latest stock price of Tesla." }
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
name: "{{agent}}",
|
|
372
|
-
content: {
|
|
373
|
-
text: "Here is the latest stock price of Tesla I found:",
|
|
374
|
-
action: "WEB_SEARCH"
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
],
|
|
378
|
-
[
|
|
379
|
-
{
|
|
380
|
-
name: "{{user}}",
|
|
381
|
-
content: {
|
|
382
|
-
text: "What are the current trending movies in the US?"
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
name: "{{agent}}",
|
|
387
|
-
content: {
|
|
388
|
-
text: "Here are the current trending movies in the US:",
|
|
389
|
-
action: "WEB_SEARCH"
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
],
|
|
393
|
-
[
|
|
394
|
-
{
|
|
395
|
-
name: "{{user}}",
|
|
396
|
-
content: {
|
|
397
|
-
text: "What is the latest score in the NBA finals?"
|
|
398
|
-
}
|
|
399
|
-
},
|
|
400
|
-
{
|
|
401
|
-
name: "{{agent}}",
|
|
402
|
-
content: {
|
|
403
|
-
text: "Here is the latest score from the NBA finals:",
|
|
404
|
-
action: "WEB_SEARCH"
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
],
|
|
408
|
-
[
|
|
409
|
-
{
|
|
410
|
-
name: "{{user}}",
|
|
411
|
-
content: { text: "When is the next Apple keynote event?" }
|
|
412
|
-
},
|
|
413
|
-
{
|
|
414
|
-
name: "{{agent}}",
|
|
415
|
-
content: {
|
|
416
|
-
text: "Here is the information about the next Apple keynote event:",
|
|
417
|
-
action: "WEB_SEARCH"
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
]
|
|
421
|
-
]
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
// src/services/tavilyService.ts
|
|
425
|
-
import { logger as logger2, Service } from "@elizaos/core";
|
|
426
|
-
import { tavily } from "@tavily/core";
|
|
427
|
-
var TavilyService = class _TavilyService extends Service {
|
|
428
|
-
static serviceType = "TAVILY";
|
|
48
|
+
var WebSearchService = class _WebSearchService extends IWebSearchService {
|
|
49
|
+
static serviceType = ServiceType.WEB_SEARCH;
|
|
50
|
+
capabilityDescription = "Web search and content discovery capabilities";
|
|
429
51
|
tavilyClient;
|
|
430
|
-
constructor(runtime) {
|
|
431
|
-
super(runtime);
|
|
432
|
-
}
|
|
433
52
|
static async start(runtime) {
|
|
434
|
-
const service = new
|
|
53
|
+
const service = new _WebSearchService(runtime);
|
|
435
54
|
await service.initialize(runtime);
|
|
436
55
|
return service;
|
|
437
56
|
}
|
|
57
|
+
async stop() {
|
|
58
|
+
}
|
|
438
59
|
async initialize(runtime) {
|
|
439
60
|
const apiKey = runtime.getSetting("TAVILY_API_KEY");
|
|
440
|
-
if (
|
|
61
|
+
if (typeof apiKey !== "string" || apiKey.length === 0) {
|
|
441
62
|
throw new Error("TAVILY_API_KEY is not set");
|
|
442
63
|
}
|
|
443
64
|
this.tavilyClient = tavily({ apiKey });
|
|
444
65
|
}
|
|
445
|
-
get capabilityDescription() {
|
|
446
|
-
return "Web search via Tavily API. Supports answer synthesis and result listing with optional images.";
|
|
447
|
-
}
|
|
448
|
-
async stop() {
|
|
449
|
-
}
|
|
450
66
|
async search(query, options) {
|
|
451
67
|
try {
|
|
452
|
-
if (!this.tavilyClient) {
|
|
453
|
-
throw new Error("TavilyService not initialized");
|
|
454
|
-
}
|
|
455
68
|
const response = await this.tavilyClient.search(query, {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
start_date: options == null ? void 0 : options.start_date,
|
|
463
|
-
end_date: options == null ? void 0 : options.end_date,
|
|
464
|
-
include_answer: (options == null ? void 0 : options.include_answer) ?? false,
|
|
465
|
-
include_raw_content: options == null ? void 0 : options.include_raw_content,
|
|
466
|
-
include_images: (options == null ? void 0 : options.include_images) ?? false,
|
|
467
|
-
include_image_descriptions: options == null ? void 0 : options.include_image_descriptions,
|
|
468
|
-
include_favicon: options == null ? void 0 : options.include_favicon,
|
|
469
|
-
include_domains: options == null ? void 0 : options.include_domains,
|
|
470
|
-
exclude_domains: options == null ? void 0 : options.exclude_domains,
|
|
471
|
-
country: options == null ? void 0 : options.country
|
|
69
|
+
includeAnswer: (options == null ? void 0 : options.includeAnswer) ?? true,
|
|
70
|
+
maxResults: (options == null ? void 0 : options.limit) ?? 3,
|
|
71
|
+
topic: (options == null ? void 0 : options.topic) ?? (options == null ? void 0 : options.type) ?? "general",
|
|
72
|
+
searchDepth: (options == null ? void 0 : options.searchDepth) ?? "basic",
|
|
73
|
+
includeImages: (options == null ? void 0 : options.includeImages) ?? false,
|
|
74
|
+
days: (options == null ? void 0 : options.days) ?? 3
|
|
472
75
|
});
|
|
473
|
-
return response;
|
|
474
|
-
} catch (
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
);
|
|
479
|
-
throw error;
|
|
76
|
+
return normalizeResponse(query, response);
|
|
77
|
+
} catch (cause) {
|
|
78
|
+
const err = cause instanceof Error ? cause : new Error(String(cause));
|
|
79
|
+
logger.error({ src: "plugin-web-search", err }, "Web search error");
|
|
80
|
+
throw err;
|
|
480
81
|
}
|
|
481
82
|
}
|
|
83
|
+
async searchNews(query, options) {
|
|
84
|
+
return this.search(query, {
|
|
85
|
+
...options,
|
|
86
|
+
type: "news",
|
|
87
|
+
topic: "news",
|
|
88
|
+
days: freshnessToDays(options == null ? void 0 : options.freshness)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async searchImages(query, options) {
|
|
92
|
+
return this.search(query, {
|
|
93
|
+
limit: options == null ? void 0 : options.limit,
|
|
94
|
+
offset: options == null ? void 0 : options.offset,
|
|
95
|
+
language: options == null ? void 0 : options.language,
|
|
96
|
+
region: options == null ? void 0 : options.region,
|
|
97
|
+
dateRange: options == null ? void 0 : options.dateRange,
|
|
98
|
+
fileType: options == null ? void 0 : options.fileType,
|
|
99
|
+
site: options == null ? void 0 : options.site,
|
|
100
|
+
sortBy: options == null ? void 0 : options.sortBy,
|
|
101
|
+
safeSearch: options == null ? void 0 : options.safeSearch,
|
|
102
|
+
includeImages: true
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async searchVideos(query, options) {
|
|
106
|
+
return this.search(query, options);
|
|
107
|
+
}
|
|
108
|
+
async getSuggestions(_query) {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
async getTrendingSearches(_region) {
|
|
112
|
+
return [];
|
|
113
|
+
}
|
|
114
|
+
async getPageInfo(url) {
|
|
115
|
+
var _a, _b;
|
|
116
|
+
const response = await fetch(url);
|
|
117
|
+
const content = await response.text();
|
|
118
|
+
const title = ((_a = content.match(/<title[^>]*>(.*?)<\/title>/i)) == null ? void 0 : _a[1]) ?? url;
|
|
119
|
+
const description = ((_b = content.match(/<meta\s+name=["']description["']\s+content=["']([^"']+)/i)) == null ? void 0 : _b[1]) ?? "";
|
|
120
|
+
return {
|
|
121
|
+
title,
|
|
122
|
+
description,
|
|
123
|
+
content,
|
|
124
|
+
metadata: {},
|
|
125
|
+
images: [],
|
|
126
|
+
links: []
|
|
127
|
+
};
|
|
128
|
+
}
|
|
482
129
|
};
|
|
483
130
|
|
|
484
131
|
// src/index.ts
|
|
132
|
+
var WEB_SEARCH_CATEGORY = {
|
|
133
|
+
category: "web",
|
|
134
|
+
label: "Web",
|
|
135
|
+
description: "Search current web pages through plugin-web-search.",
|
|
136
|
+
contexts: ["knowledge", "browser"],
|
|
137
|
+
filters: [
|
|
138
|
+
{
|
|
139
|
+
name: "topic",
|
|
140
|
+
label: "Topic",
|
|
141
|
+
description: "Tavily search topic.",
|
|
142
|
+
type: "enum",
|
|
143
|
+
options: [
|
|
144
|
+
{ label: "General", value: "general" },
|
|
145
|
+
{ label: "News", value: "news" }
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
name: "searchDepth",
|
|
150
|
+
label: "Search depth",
|
|
151
|
+
description: "Tavily search depth.",
|
|
152
|
+
type: "enum",
|
|
153
|
+
options: [
|
|
154
|
+
{ label: "Basic", value: "basic" },
|
|
155
|
+
{ label: "Advanced", value: "advanced" }
|
|
156
|
+
]
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: "includeImages",
|
|
160
|
+
label: "Include images",
|
|
161
|
+
description: "Include image results when available.",
|
|
162
|
+
type: "boolean"
|
|
163
|
+
}
|
|
164
|
+
],
|
|
165
|
+
resultSchemaSummary: "SearchResponse with query, answer, results containing title/url/description/content/score, and optional images.",
|
|
166
|
+
capabilities: ["web", "news", "current-information"],
|
|
167
|
+
source: "plugin-web-search",
|
|
168
|
+
serviceType: ServiceType2.WEB_SEARCH
|
|
169
|
+
};
|
|
170
|
+
function registerWebSearchCategory(runtime) {
|
|
171
|
+
try {
|
|
172
|
+
runtime.getSearchCategory(WEB_SEARCH_CATEGORY.category, {
|
|
173
|
+
includeDisabled: true
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
} catch {
|
|
177
|
+
runtime.registerSearchCategory(WEB_SEARCH_CATEGORY);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
485
180
|
var webSearchPlugin = {
|
|
486
181
|
name: "webSearch",
|
|
487
|
-
description: "Search the web
|
|
488
|
-
|
|
489
|
-
|
|
182
|
+
description: "Search the web and get news",
|
|
183
|
+
init: async (_config, runtime) => {
|
|
184
|
+
registerWebSearchCategory(runtime);
|
|
185
|
+
},
|
|
186
|
+
actions: [],
|
|
490
187
|
providers: [],
|
|
491
|
-
services: [
|
|
492
|
-
clients: [],
|
|
493
|
-
adapters: []
|
|
188
|
+
services: [WebSearchService]
|
|
494
189
|
};
|
|
495
190
|
var index_default = webSearchPlugin;
|
|
496
191
|
export {
|
|
192
|
+
WEB_SEARCH_CATEGORY,
|
|
497
193
|
index_default as default,
|
|
194
|
+
registerWebSearchCategory,
|
|
498
195
|
webSearchPlugin
|
|
499
196
|
};
|
|
500
197
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/actions/webSearch.ts","../src/services/tavilyService.ts","../src/index.ts"],"sourcesContent":["import {\n type ActionResult,\n type HandlerCallback,\n type IAgentRuntime,\n type Memory,\n type State,\n logger,\n Action,\n ModelType,\n composePromptFromState,\n parseKeyValueXml,\n} from \"@elizaos/core\";\nimport { TavilyService } from \"../services/tavilyService\";\nimport type { SearchResult } from \"../types\";\n\ninterface WebSearchParams {\n query?: string;\n topic?: string;\n source?: string;\n max_results?: number;\n search_depth?: string;\n time_range?: \"day\" | \"week\" | \"month\" | \"year\" | \"d\" | \"w\" | \"m\" | \"y\";\n start_date?: string;\n end_date?: string;\n}\n\nconst DEFAULT_MAX_WEB_SEARCH_CHARS = 16000;\n\n/**\n * Build the extraction template with the appropriate conversation context.\n * Prefers conversationLog if available, falls back to recentMessages.\n */\nfunction buildExtractionTemplate(conversationContext: string): string {\n return `# Web Search Parameter Extraction\n\n## Recent Conversation\n${conversationContext}\n\n## Task\nExtract the search query and parameters from the conversation above. The user wants to search the web for information.\n\n## Instructions\n- Determine the exact search query the user wants\n- If the topic is about crypto, DeFi, finance, or markets, set topic to \"finance\"\n- Only include optional parameters if clearly indicated by the user\n\nRespond ONLY with the following XML format:\n<response>\n <reasoning>\n Step 1: Identify what information the user is looking for\n Step 2: Determine the best search query to find this information\n Step 3: Classify the topic based on the subject matter\n </reasoning>\n <query>the exact query to search</query>\n <topic>general or finance</topic>\n</response>`;\n}\n\nfunction MaxTokens(\n data: string,\n maxTokens: number = DEFAULT_MAX_WEB_SEARCH_CHARS,\n): string {\n // Character-based truncation to cap response length\n return data.length > maxTokens ? data.slice(0, maxTokens) : data;\n}\n\n/**\n * Extract search parameters from state or via LLM.\n * Supports multiple runtime patterns:\n * 1. actionParams from custom bootstrap (otaku pattern)\n * 2. LLM extraction from conversation context (eliza-cloud-v2, elizav2)\n */\nasync function extractSearchParams(\n runtime: IAgentRuntime,\n message: Memory,\n _state: State,\n): Promise<WebSearchParams> {\n // First, try to get params from state (custom bootstrap pattern)\n const composedState = await runtime.composeState(\n message,\n [\"ACTION_STATE\"],\n true,\n );\n\n const stateParams = (composedState?.data?.actionParams ||\n composedState?.data?.webSearch ||\n composedState?.data?.websearch ||\n {}) as WebSearchParams;\n\n // If we have a query from state, use it\n if (stateParams?.query?.trim()) {\n logger.info(\n { src: \"webSearch:extractParams\", source: \"actionParams\" },\n \"Using params from state\",\n );\n return stateParams;\n }\n\n // Otherwise, extract from conversation using LLM\n logger.info(\n { src: \"webSearch:extractParams\", source: \"llm\" },\n \"Extracting params via LLM\",\n );\n\n try {\n // Compose state - try to get both conversationLog and recentMessages\n const extractionState = await runtime.composeState(\n message,\n [\"RECENT_MESSAGES\"],\n true,\n );\n\n // Prefer conversationLog if available, fallback to recentMessages\n const conversationLog = extractionState?.values?.conversationLog;\n const recentMessages = extractionState?.values?.recentMessages;\n\n const conversationContext =\n (typeof conversationLog === \"string\" && conversationLog.trim()) ||\n (typeof recentMessages === \"string\" && recentMessages.trim()) ||\n \"\";\n\n const contextSource =\n typeof conversationLog === \"string\" && conversationLog.trim()\n ? \"conversationLog\"\n : \"recentMessages\";\n\n logger.debug(\n { src: \"webSearch:extractParams\", contextSource },\n \"Using conversation context\",\n );\n\n const template = buildExtractionTemplate(conversationContext);\n const prompt = composePromptFromState({\n state: extractionState,\n template,\n });\n\n const response = await runtime.useModel(ModelType.TEXT_SMALL, { prompt });\n const parsed = parseKeyValueXml(response || \"\");\n\n if (parsed?.query) {\n const extractedParams: WebSearchParams = {\n query: String(parsed.query).trim(),\n topic:\n parsed.topic === \"finance\"\n ? \"finance\"\n : (\"general\" as \"general\" | \"finance\"),\n };\n\n logger.info(\n { src: \"webSearch:extractParams\", query: extractedParams.query },\n \"Extracted query via LLM\",\n );\n\n return extractedParams;\n }\n } catch (err) {\n logger.warn(\n { src: \"webSearch:extractParams\", error: (err as Error).message },\n \"LLM extraction failed, falling back to message text\",\n );\n }\n\n // Final fallback: use the message text directly as the query\n const messageText = message.content?.text?.trim();\n if (messageText) {\n logger.info(\n { src: \"webSearch:extractParams\", source: \"messageText\" },\n \"Using message text as query\",\n );\n return { query: messageText };\n }\n\n return {};\n}\n\nexport const webSearch: Action = {\n name: \"WEB_SEARCH\",\n similes: [\n \"SEARCH_WEB\",\n \"INTERNET_SEARCH\",\n \"LOOKUP\",\n \"QUERY_WEB\",\n \"FIND_ONLINE\",\n \"SEARCH_ENGINE\",\n \"WEB_LOOKUP\",\n \"ONLINE_SEARCH\",\n \"FIND_INFORMATION\",\n ],\n suppressInitialMessage: true,\n description:\n \"Search the web using Tavily. Supports general web search and finance topics (crypto/DeFi/markets). Use when other actions/providers can't provide accurate or current info.\\n\\n\" +\n \"IMPORTANT - Result Quality Check:\\n\" +\n \"- If search returns off-topic or poor results, RETRY with parameter adjustments in the SAME round\\n\" +\n \"- Try: topic='finance' for crypto/markets, source filter (theblock.com, coindesk.com), broader time_range, advanced search_depth, or rephrased query\\n\" +\n \"- For crypto/DeFi content: use topic='finance' + source from [theblock.com, coindesk.com, decrypt.co, dlnews.com]\\n\" +\n \"- Don't give up after one attempt if results are clearly irrelevant\",\n\n // Parameter schema for tool calling\n parameters: {\n query: {\n type: \"string\",\n description: \"The search query to look up on the web\",\n required: true,\n },\n topic: {\n type: \"string\",\n description:\n \"Search topic: 'general' for web search, 'finance' for financial/crypto/DeFi content. Defaults to 'general'.\",\n required: false,\n },\n source: {\n type: \"string\",\n description:\n \"Specific source domain to limit results (e.g., 'bloomberg.com', 'reuters.com'). Uses site: operator.\",\n required: false,\n },\n max_results: {\n type: \"number\",\n description: \"Maximum number of results to return (1-20). Defaults to 5.\",\n required: false,\n },\n search_depth: {\n type: \"string\",\n description:\n \"Search depth: 'basic' for quick results or 'advanced' for comprehensive search. Defaults to 'basic'.\",\n required: false,\n },\n time_range: {\n type: \"string\",\n description:\n \"Time range filter: 'day', 'week', 'month', 'year' (or 'd', 'w', 'm', 'y')\",\n required: false,\n },\n start_date: {\n type: \"string\",\n description:\n \"Start date filter in YYYY-MM-DD format (returns results after this date)\",\n required: false,\n },\n end_date: {\n type: \"string\",\n description:\n \"End date filter in YYYY-MM-DD format (returns results before this date)\",\n required: false,\n },\n },\n\n validate: async (\n runtime: IAgentRuntime,\n _message: Memory,\n _state?: State,\n ) => {\n try {\n const service = runtime.getService<TavilyService>(\"TAVILY\");\n return !!service;\n } catch (err) {\n logger.warn(\n { src: \"webSearch:validate\", error: (err as Error).message },\n \"TavilyService not available\",\n );\n return false;\n }\n },\n handler: async (\n runtime: IAgentRuntime,\n message: Memory,\n state: State,\n _options?: { [key: string]: unknown },\n callback?: HandlerCallback,\n ): Promise<ActionResult> => {\n try {\n const tavilyService = runtime.getService<TavilyService>(\"TAVILY\");\n if (!tavilyService) {\n throw new Error(\"TavilyService not initialized\");\n }\n\n // Extract parameters (supports multiple runtime patterns)\n const params = await extractSearchParams(runtime, message, state);\n\n // Extract and validate query parameter (required)\n const query: string | undefined = params?.query?.trim();\n\n if (!query) {\n const errorMsg =\n \"Missing required parameter 'query'. Please specify what to search for.\";\n logger.error({ src: \"webSearch:handler\" }, errorMsg);\n const emptyResult: ActionResult = {\n text: errorMsg,\n success: false,\n data: {\n actionName: \"WEB_SEARCH\",\n },\n error: \"missing_required_parameter\",\n };\n if (callback) {\n callback({\n text: emptyResult.text,\n content: { error: \"missing_required_parameter\", details: errorMsg },\n });\n }\n return emptyResult;\n }\n\n const source = params?.source?.trim();\n const topic = params?.topic === \"finance\" ? \"finance\" : \"general\";\n const maxResults = params?.max_results\n ? Math.min(Math.max(1, params.max_results), 20)\n : 5;\n const searchDepth =\n params?.search_depth === \"advanced\" ? \"advanced\" : \"basic\";\n\n // Build enhanced query with source if provided\n let enhancedQuery = query;\n if (source) {\n enhancedQuery = `${query} site:${source}`;\n logger.info(\n { src: \"webSearch:handler\", source },\n \"Searching with source filter\",\n );\n }\n\n logger.info(\n { src: \"webSearch:handler\", query: enhancedQuery, topic },\n \"Executing web search\",\n );\n\n // Store input parameters for return\n const inputParams = {\n query,\n topic,\n source,\n max_results: maxResults,\n search_depth: searchDepth,\n time_range: params?.time_range,\n start_date: params?.start_date,\n end_date: params?.end_date,\n };\n\n // Use provided parameters or defaults\n const searchResponse = await tavilyService.search(enhancedQuery, {\n topic,\n max_results: maxResults,\n search_depth: searchDepth,\n time_range: params?.time_range,\n start_date: params?.start_date,\n end_date: params?.end_date,\n include_answer: true,\n include_images: false,\n });\n\n if (searchResponse && searchResponse.results.length) {\n const responseList = searchResponse.answer\n ? `${searchResponse.answer}${\n Array.isArray(searchResponse.results) &&\n searchResponse.results.length > 0\n ? `\\n\\nFor more details, you can check out these resources:\\n${searchResponse.results\n .map(\n (result: SearchResult, index: number) =>\n `${index + 1}. [${result.title}](${result.url})`,\n )\n .join(\"\\n\")}`\n : \"\"\n }`\n : \"\";\n\n const result: ActionResult = {\n text: MaxTokens(responseList, DEFAULT_MAX_WEB_SEARCH_CHARS),\n success: true,\n data: {\n ...searchResponse,\n actionName: \"WEB_SEARCH\",\n },\n input: inputParams,\n } as ActionResult & { input: typeof inputParams };\n\n if (callback) {\n callback({\n text: result.text,\n actions: [\"WEB_SEARCH\"],\n data: result.data,\n });\n }\n\n return result;\n }\n\n const noResult: ActionResult = {\n text: \"I couldn't find relevant results for that query.\",\n success: false,\n data: {\n actionName: \"WEB_SEARCH\",\n },\n input: inputParams,\n } as ActionResult & { input: typeof inputParams };\n\n if (callback) {\n callback({ text: noResult.text });\n }\n return noResult;\n } catch (error) {\n const errMsg = error instanceof Error ? error.message : String(error);\n logger.error(\n { src: \"webSearch:handler\", error: errMsg },\n \"Action failed\",\n );\n\n const errorResult: ActionResult = {\n text: `Web search failed: ${errMsg}`,\n success: false,\n data: {\n actionName: \"WEB_SEARCH\",\n },\n error: errMsg,\n };\n\n if (callback) {\n callback({\n text: errorResult.text,\n content: { error: \"web_search_failed\", details: errMsg },\n });\n }\n return errorResult;\n }\n },\n examples: [\n [\n {\n name: \"{{user}}\",\n content: {\n text: \"Latest Aave news\",\n },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Let me search for Aave news from crypto sources:\",\n action: \"WEB_SEARCH\",\n actionParams: {\n query: \"Aave protocol\",\n topic: \"finance\",\n source: \"theblock.com\",\n time_range: \"week\",\n },\n },\n },\n ],\n [\n {\n name: \"{{user}}\",\n content: {\n text: \"Find the latest news about SpaceX launches.\",\n },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Here is the latest news about SpaceX launches:\",\n action: \"WEB_SEARCH\",\n },\n },\n ],\n [\n {\n name: \"{{user}}\",\n content: {\n text: \"Can you find details about the iPhone 16 release?\",\n },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Here are the details I found about the iPhone 16 release:\",\n action: \"WEB_SEARCH\",\n },\n },\n ],\n [\n {\n name: \"{{user}}\",\n content: {\n text: \"What is the schedule for the next FIFA World Cup?\",\n },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Here is the schedule for the next FIFA World Cup:\",\n action: \"WEB_SEARCH\",\n },\n },\n ],\n [\n {\n name: \"{{user}}\",\n content: { text: \"Check the latest stock price of Tesla.\" },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Here is the latest stock price of Tesla I found:\",\n action: \"WEB_SEARCH\",\n },\n },\n ],\n [\n {\n name: \"{{user}}\",\n content: {\n text: \"What are the current trending movies in the US?\",\n },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Here are the current trending movies in the US:\",\n action: \"WEB_SEARCH\",\n },\n },\n ],\n [\n {\n name: \"{{user}}\",\n content: {\n text: \"What is the latest score in the NBA finals?\",\n },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Here is the latest score from the NBA finals:\",\n action: \"WEB_SEARCH\",\n },\n },\n ],\n [\n {\n name: \"{{user}}\",\n content: { text: \"When is the next Apple keynote event?\" },\n },\n {\n name: \"{{agent}}\",\n content: {\n text: \"Here is the information about the next Apple keynote event:\",\n action: \"WEB_SEARCH\",\n },\n },\n ],\n ],\n};\n","import { IAgentRuntime, logger, Service } from \"@elizaos/core\";\nimport { tavily } from \"@tavily/core\";\nimport type { ITavilyService, SearchOptions, SearchResponse } from \"../types\";\n\nexport type TavilyClient = ReturnType<typeof tavily>;\n\nexport class TavilyService extends Service implements ITavilyService {\n static serviceType = \"TAVILY\" as const;\n\n private tavilyClient!: TavilyClient;\n\n constructor(runtime: IAgentRuntime) {\n super(runtime);\n }\n\n static async start(runtime: IAgentRuntime): Promise<TavilyService> {\n const service = new TavilyService(runtime);\n await service.initialize(runtime);\n return service;\n }\n\n async initialize(runtime: IAgentRuntime): Promise<void> {\n const apiKey = runtime.getSetting(\"TAVILY_API_KEY\") as string;\n if (!apiKey) {\n throw new Error(\"TAVILY_API_KEY is not set\");\n }\n this.tavilyClient = tavily({ apiKey });\n }\n\n get capabilityDescription(): string {\n return \"Web search via Tavily API. Supports answer synthesis and result listing with optional images.\";\n }\n\n async stop(): Promise<void> {\n // No persistent connections to close for Tavily client\n }\n\n async search(\n query: string,\n options?: SearchOptions,\n ): Promise<SearchResponse> {\n try {\n if (!this.tavilyClient) {\n throw new Error(\"TavilyService not initialized\");\n }\n\n const response = await this.tavilyClient.search(query, {\n auto_parameters: options?.auto_parameters,\n topic: options?.topic ?? \"general\",\n search_depth: options?.search_depth ?? \"basic\",\n chunks_per_source: options?.chunks_per_source,\n max_results: options?.max_results ?? 5,\n time_range: options?.time_range,\n start_date: options?.start_date,\n end_date: options?.end_date,\n include_answer: options?.include_answer ?? false,\n include_raw_content: options?.include_raw_content,\n include_images: options?.include_images ?? false,\n include_image_descriptions: options?.include_image_descriptions,\n include_favicon: options?.include_favicon,\n include_domains: options?.include_domains,\n exclude_domains: options?.exclude_domains,\n country: options?.country,\n });\n\n return response;\n } catch (error) {\n logger.error(\n { src: \"tavilyService:search\", error: (error as Error).message },\n \"Tavily search error\",\n );\n throw error;\n }\n }\n}\n","import { webSearch } from \"./actions/webSearch\";\nimport { TavilyService } from \"./services/tavilyService\";\n\nexport const webSearchPlugin = {\n name: \"webSearch\",\n description: \"Search the web using Tavily\",\n actions: [webSearch],\n evaluators: [],\n providers: [],\n services: [TavilyService],\n clients: [],\n adapters: [],\n};\n\nexport default webSearchPlugin;\n"],"mappings":"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","names":["result","logger"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/services/webSearchService.ts"],"sourcesContent":["import type { IAgentRuntime, Plugin, SearchCategoryRegistration } from \"@elizaos/core\";\nimport { ServiceType } from \"@elizaos/core\";\n\nimport { WebSearchService } from \"./services/webSearchService\";\n\nexport const WEB_SEARCH_CATEGORY: SearchCategoryRegistration = {\n category: \"web\",\n label: \"Web\",\n description: \"Search current web pages through plugin-web-search.\",\n contexts: [\"knowledge\", \"browser\"],\n filters: [\n {\n name: \"topic\",\n label: \"Topic\",\n description: \"Tavily search topic.\",\n type: \"enum\",\n options: [\n { label: \"General\", value: \"general\" },\n { label: \"News\", value: \"news\" },\n ],\n },\n {\n name: \"searchDepth\",\n label: \"Search depth\",\n description: \"Tavily search depth.\",\n type: \"enum\",\n options: [\n { label: \"Basic\", value: \"basic\" },\n { label: \"Advanced\", value: \"advanced\" },\n ],\n },\n {\n name: \"includeImages\",\n label: \"Include images\",\n description: \"Include image results when available.\",\n type: \"boolean\",\n },\n ],\n resultSchemaSummary:\n \"SearchResponse with query, answer, results containing title/url/description/content/score, and optional images.\",\n capabilities: [\"web\", \"news\", \"current-information\"],\n source: \"plugin-web-search\",\n serviceType: ServiceType.WEB_SEARCH,\n};\n\nexport function registerWebSearchCategory(runtime: IAgentRuntime): void {\n try {\n runtime.getSearchCategory(WEB_SEARCH_CATEGORY.category, {\n includeDisabled: true,\n });\n return;\n } catch {\n runtime.registerSearchCategory(WEB_SEARCH_CATEGORY);\n }\n}\n\nexport const webSearchPlugin: Plugin = {\n name: \"webSearch\",\n description: \"Search the web and get news\",\n init: async (_config, runtime) => {\n registerWebSearchCategory(runtime);\n },\n actions: [],\n providers: [],\n services: [WebSearchService],\n};\n\nexport default webSearchPlugin;\n","import { type IAgentRuntime, IWebSearchService, logger, ServiceType } from \"@elizaos/core\";\nimport { tavily } from \"@tavily/core\";\n\nimport type {\n ImageSearchOptions,\n NewsSearchOptions,\n SearchOptions,\n SearchResponse,\n VideoSearchOptions,\n} from \"../types\";\n\nexport type TavilyClient = ReturnType<typeof tavily>;\n\ntype TavilySearchResult = {\n title?: string;\n url?: string;\n content?: string;\n rawContent?: string;\n score?: number;\n publishedDate?: string;\n};\n\ntype TavilySearchResponse = {\n answer?: string;\n query?: string;\n responseTime?: number;\n images?: Array<{ url?: string; description?: string } | string>;\n results?: TavilySearchResult[];\n};\n\nfunction parsePublishedDate(value: string | undefined): Date | undefined {\n if (!value) return undefined;\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? undefined : date;\n}\n\nfunction normalizeResponse(query: string, response: TavilySearchResponse): SearchResponse {\n const results = (response.results ?? []).map((result) => {\n const content = result.content ?? \"\";\n return {\n title: result.title ?? \"Untitled\",\n url: result.url ?? \"\",\n description: content,\n content,\n rawContent: result.rawContent,\n score: typeof result.score === \"number\" ? result.score : 0,\n publishedDate: parsePublishedDate(result.publishedDate),\n };\n });\n const images = (response.images ?? [])\n .map((image) =>\n typeof image === \"string\"\n ? { url: image }\n : { url: image.url ?? \"\", description: image.description }\n )\n .filter((image) => image.url);\n\n return {\n answer: response.answer,\n query: response.query ?? query,\n responseTime: response.responseTime,\n images,\n results,\n };\n}\n\nfunction freshnessToDays(freshness: NewsSearchOptions[\"freshness\"]): number {\n switch (freshness) {\n case \"day\":\n return 1;\n case \"week\":\n return 7;\n case \"month\":\n return 30;\n default:\n return 3;\n }\n}\n\nexport class WebSearchService extends IWebSearchService {\n static override serviceType = ServiceType.WEB_SEARCH;\n override capabilityDescription = \"Web search and content discovery capabilities\" as const;\n\n tavilyClient!: TavilyClient;\n\n static override async start(runtime: IAgentRuntime): Promise<WebSearchService> {\n const service = new WebSearchService(runtime);\n await service.initialize(runtime);\n return service;\n }\n\n async stop(): Promise<void> {\n // Tavily client is stateless HTTP; nothing to tear down.\n }\n\n private async initialize(runtime: IAgentRuntime): Promise<void> {\n const apiKey = runtime.getSetting(\"TAVILY_API_KEY\");\n if (typeof apiKey !== \"string\" || apiKey.length === 0) {\n throw new Error(\"TAVILY_API_KEY is not set\");\n }\n this.tavilyClient = tavily({ apiKey });\n }\n\n async search(query: string, options?: SearchOptions): Promise<SearchResponse> {\n try {\n const response = await this.tavilyClient.search(query, {\n includeAnswer: options?.includeAnswer ?? true,\n maxResults: options?.limit ?? 3,\n topic: options?.topic ?? options?.type ?? \"general\",\n searchDepth: options?.searchDepth ?? \"basic\",\n includeImages: options?.includeImages ?? false,\n days: options?.days ?? 3,\n });\n\n return normalizeResponse(query, response as TavilySearchResponse);\n } catch (cause) {\n const err = cause instanceof Error ? cause : new Error(String(cause));\n logger.error({ src: \"plugin-web-search\", err }, \"Web search error\");\n throw err;\n }\n }\n\n async searchNews(query: string, options?: NewsSearchOptions): Promise<SearchResponse> {\n return this.search(query, {\n ...options,\n type: \"news\",\n topic: \"news\",\n days: freshnessToDays(options?.freshness),\n });\n }\n\n async searchImages(query: string, options?: ImageSearchOptions): Promise<SearchResponse> {\n return this.search(query, {\n limit: options?.limit,\n offset: options?.offset,\n language: options?.language,\n region: options?.region,\n dateRange: options?.dateRange,\n fileType: options?.fileType,\n site: options?.site,\n sortBy: options?.sortBy,\n safeSearch: options?.safeSearch,\n includeImages: true,\n });\n }\n\n async searchVideos(query: string, options?: VideoSearchOptions): Promise<SearchResponse> {\n return this.search(query, options);\n }\n\n async getSuggestions(_query: string): Promise<string[]> {\n return [];\n }\n\n async getTrendingSearches(_region?: string): Promise<string[]> {\n return [];\n }\n\n async getPageInfo(url: string): Promise<{\n title: string;\n description: string;\n content: string;\n metadata: Record<string, string>;\n images: string[];\n links: string[];\n }> {\n const response = await fetch(url);\n const content = await response.text();\n const title = content.match(/<title[^>]*>(.*?)<\\/title>/i)?.[1] ?? url;\n const description =\n content.match(/<meta\\s+name=[\"']description[\"']\\s+content=[\"']([^\"']+)/i)?.[1] ?? \"\";\n return {\n title,\n description,\n content,\n metadata: {},\n images: [],\n links: [],\n };\n }\n}\n"],"mappings":"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","names":["ServiceType","ServiceType"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elizaos/plugin-web-search",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
"./package.json": "./package.json",
|
|
10
10
|
".": {
|
|
11
11
|
"import": {
|
|
12
|
-
"@elizaos/source": "./src/index.ts",
|
|
13
12
|
"types": "./dist/index.d.ts",
|
|
14
13
|
"default": "./dist/index.js"
|
|
15
14
|
}
|
|
@@ -19,31 +18,22 @@
|
|
|
19
18
|
"dist"
|
|
20
19
|
],
|
|
21
20
|
"dependencies": {
|
|
22
|
-
"@
|
|
21
|
+
"@elizaos/core": "2.0.0-beta.1",
|
|
22
|
+
"@tavily/core": "^0.7.0",
|
|
23
|
+
"js-tiktoken": "1.0.21",
|
|
24
|
+
"tsup": "^8.5.1"
|
|
23
25
|
},
|
|
24
26
|
"devDependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"@eslint/js": "^9.17.0",
|
|
27
|
-
"@typescript-eslint/eslint-plugin": "^8.22.0",
|
|
28
|
-
"@typescript-eslint/parser": "^8.22.0",
|
|
29
|
-
"eslint": "^9.17.0",
|
|
30
|
-
"prettier": "3.5.3",
|
|
31
|
-
"tsup": "8.4.0"
|
|
32
|
-
},
|
|
33
|
-
"peerDependencies": {
|
|
34
|
-
"@elizaos/core": "1.7.0",
|
|
35
|
-
"whatwg-url": "7.1.0"
|
|
27
|
+
"@biomejs/biome": "2.4.14"
|
|
36
28
|
},
|
|
37
29
|
"scripts": {
|
|
38
30
|
"build": "tsup --format esm --dts",
|
|
39
31
|
"dev": "tsup --format esm --dts --watch",
|
|
40
|
-
"lint": "
|
|
41
|
-
"lint:
|
|
42
|
-
"format": "
|
|
43
|
-
"format:fix": "
|
|
44
|
-
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"access": "public"
|
|
32
|
+
"lint": "biome check src/",
|
|
33
|
+
"lint:fix": "biome check --write src/",
|
|
34
|
+
"format": "biome format src/",
|
|
35
|
+
"format:fix": "biome format --write src/",
|
|
36
|
+
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
47
37
|
},
|
|
48
38
|
"agentConfig": {
|
|
49
39
|
"pluginType": "elizaos:client:1.0.0",
|