@elizaos/plugin-web-search 1.0.1 → 2.0.3-beta.5

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shaw Walters and elizaOS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,247 +1,116 @@
1
1
  # @elizaos/plugin-web-search
2
2
 
3
- A plugin for powerful web search capabilities, providing efficient search query handling and result processing through a customizable API interface.
3
+ Adds live web search to an Eliza agent via the [Tavily](https://tavily.com/) API.
4
4
 
5
- ## Overview
5
+ ## What it does
6
6
 
7
- This plugin provides functionality to:
7
+ Installing this plugin registers a `WebSearchService` (`ServiceType.WEB_SEARCH`) that any other plugin or action can call to search the web. It also registers the `"web"` search category so the elizaOS core search-dispatch layer routes web queries to this service automatically.
8
8
 
9
- - Execute web search queries with customizable parameters
10
- - Process and format search results
11
- - Handle search API authentication
12
- - Manage token limits and response sizes
13
- - Optimize query performance
9
+ Capabilities exposed through the service:
14
10
 
15
- ## Installation
11
+ - **General web search** — ranked results with optional AI-generated answer.
12
+ - **News search** — topic-filtered results with freshness control (day / week / month).
13
+ - **Image search** — includes image URLs in results.
14
+ - **Video search** — delegates to general search (Tavily has no separate video endpoint).
15
+ - **Page info** — fetches a URL and extracts title, description, and raw HTML content.
16
16
 
17
- ```bash
18
- pnpm install @elizaos/plugin-web-search
19
- ```
17
+ No actions are registered by the plugin itself. Other plugins that rely on web search call `runtime.getService(ServiceType.WEB_SEARCH)` and invoke the service directly.
20
18
 
21
- ## Configuration
19
+ ## Installation
22
20
 
23
- The plugin requires the following environment variables:
21
+ Add the package to your agent:
24
22
 
25
- ```env
26
- TAVILY_API_KEY=your_api_key # Required: API key for search service
23
+ ```bash
24
+ bun add @elizaos/plugin-web-search
27
25
  ```
28
26
 
29
- ## Usage
30
-
31
- Import and register the plugin in your Eliza configuration.
27
+ Then include it in your character config:
32
28
 
33
29
  ```typescript
34
30
  import { webSearchPlugin } from "@elizaos/plugin-web-search";
35
31
 
36
32
  export default {
37
33
  plugins: [webSearchPlugin],
38
- // ... other configuration
34
+ // ...
39
35
  };
40
36
  ```
41
37
 
42
- **Custom Usage**
43
- If you want custom usage, for example, a social media client to search the web before posting, you can also import the webSearchService and use it directly. Here's how you can do it:
44
-
45
- ```typescript
46
- // Example usage in a social media client
47
- const webSearchService = new WebSearchService();
48
- await webSearchService.initialize(runtime);
49
- const latestNews = await webSearchService.search(
50
- "latest news on AI Agents",
51
- // searchOptions
52
- );
53
-
54
- const state = await this.runtime.composeState(
55
- { } // memory,
56
- { // additional keys
57
- latestNews: latestNews,
58
- }
59
- );
60
-
61
- // Then modify the post template to include the {{latestNews}} and however you need
62
- ```
63
-
64
- ## Features
38
+ ## Configuration
65
39
 
66
- ### Web Search
40
+ | Environment variable | Required | Description |
41
+ |---------------------|----------|-------------|
42
+ | `TAVILY_API_KEY` | Yes | API key from [app.tavily.com](https://app.tavily.com). Without it the service starts in a degraded (inert) state and throws a descriptive error on first use. |
67
43
 
68
- The plugin provides comprehensive web search capabilities:
44
+ Set the key in your environment or agent settings:
69
45
 
70
- ```typescript
71
- import { webSearch } from "@elizaos/plugin-web-search";
72
-
73
- // Execute a search query
74
- const result = await webSearch.handler(
75
- runtime,
76
- {
77
- content: { text: "What is the latest news about AI?" },
78
- },
79
- state,
80
- {},
81
- callback
82
- );
46
+ ```env
47
+ TAVILY_API_KEY=tvly-...
83
48
  ```
84
49
 
85
- ### Token Management
50
+ ## Calling the service from another plugin
86
51
 
87
52
  ```typescript
88
- // The plugin automatically handles token limits
89
- const DEFAULT_MAX_WEB_SEARCH_TOKENS = 4000;
53
+ import { ServiceType } from "@elizaos/core";
54
+ import type { IWebSearchService } from "@elizaos/core";
90
55
 
91
- // Example of token-limited response
92
- const response = MaxTokens(searchResult, DEFAULT_MAX_WEB_SEARCH_TOKENS);
93
- ```
56
+ const svc = runtime.getService<IWebSearchService>(ServiceType.WEB_SEARCH);
94
57
 
95
- ## Development
96
-
97
- ### Building
98
-
99
- ```bash
100
- pnpm run build
101
- ```
58
+ // General search
59
+ const result = await svc.search("latest developments in open-source LLMs", {
60
+ limit: 5,
61
+ searchDepth: "advanced",
62
+ includeAnswer: true,
63
+ });
102
64
 
103
- ### Testing
65
+ // News search
66
+ const news = await svc.searchNews("AI regulation", { freshness: "week" });
104
67
 
105
- ```bash
106
- pnpm run test
68
+ // Image search (always returns image results; no flag needed)
69
+ const images = await svc.searchImages("northern lights", { limit: 10 });
107
70
  ```
108
71
 
109
- ### Development Mode
110
-
111
- ```bash
112
- pnpm run dev
113
- ```
114
-
115
- ## Dependencies
116
-
117
- - `@elizaos/core`: Core Eliza functionality
118
- - `js-tiktoken`: Token counting and management
119
- - `tsup`: Build tool
120
- - Other standard dependencies listed in package.json
121
-
122
- ## API Reference
123
-
124
- ### Core Interfaces
72
+ `SearchResponse` shape:
125
73
 
126
74
  ```typescript
127
- interface Action {
128
- name: "WEB_SEARCH";
129
- similes: string[];
130
- description: string;
131
- validate: (runtime: IAgentRuntime, message: Memory) => Promise<boolean>;
132
- handler: (
133
- runtime: IAgentRuntime,
134
- message: Memory,
135
- state: State,
136
- options: any,
137
- callback: HandlerCallback
138
- ) => Promise<void>;
139
- examples: Array<Array<any>>;
140
- }
141
-
142
- interface SearchResult {
143
- title: string;
144
- url: string;
145
- answer?: string;
146
- results?: Array<{
75
+ {
76
+ query: string;
77
+ answer?: string; // AI-generated summary (when includeAnswer is true)
78
+ responseTime?: number;
79
+ results: Array<{
147
80
  title: string;
148
81
  url: string;
82
+ description: string;
83
+ content: string;
84
+ rawContent?: string;
85
+ score: number;
86
+ publishedDate?: Date;
149
87
  }>;
88
+ images: Array<{ url: string; description?: string }>;
150
89
  }
151
90
  ```
152
91
 
153
- ### Plugin Methods
154
-
155
- - `webSearch.handler`: Main method for executing searches
156
- - `generateWebSearch`: Core search generation function
157
- - `MaxTokens`: Token limit management function
158
- - `getTotalTokensFromString`: Token counting utility
159
-
160
- ## Common Issues/Troubleshooting
161
-
162
- ### Issue: API Authentication Failures
163
-
164
- - **Cause**: Invalid or missing Tavily API key
165
- - **Solution**: Verify TAVILY_API_KEY environment variable
166
-
167
- ### Issue: Token Limit Exceeded
168
-
169
- - **Cause**: Search results exceeding maximum token limit
170
- - **Solution**: Results are automatically truncated to fit within limits
171
-
172
- ### Issue: Search Rate Limiting
173
-
174
- - **Cause**: Too many requests in short time
175
- - **Solution**: Implement proper request throttling
176
-
177
- ## Security Best Practices
178
-
179
- - Store API keys securely using environment variables
180
- - Validate all search inputs
181
- - Implement proper error handling
182
- - Keep dependencies updated
183
- - Monitor API usage and rate limits
184
- - Use HTTPS for API communication
185
-
186
- ## Example Usage
187
-
188
- ```typescript
189
- // Basic search
190
- const searchQuery = "Latest developments in quantum computing";
191
- const results = await generateWebSearch(searchQuery, runtime);
192
-
193
- // With formatted response
194
- if (results && results.results.length) {
195
- const formattedResponse = `${results.answer}\n\nFor more details, check out:\n${results.results
196
- .map(
197
- (result, index) => `${index + 1}. [${result.title}](${result.url})`
198
- )
199
- .join("\n")}`;
200
- }
201
- ```
92
+ ## Search options
202
93
 
203
- ## Configuration Options
94
+ | Option | Type | Default | Description |
95
+ |--------|------|---------|-------------|
96
+ | `limit` | number | 3 | Maximum number of results |
97
+ | `topic` / `type` | `"general"` \| `"news"` | `"general"` | Tavily topic filter |
98
+ | `searchDepth` | `"basic"` \| `"advanced"` | `"basic"` | Tavily crawl depth |
99
+ | `includeAnswer` | boolean | true | Request an AI-generated answer |
100
+ | `includeImages` | boolean | false | Include image results |
101
+ | `days` | number | 3 | Freshness window in days (news searches) |
204
102
 
205
- ### Token Management
103
+ ## Development
206
104
 
207
- ```typescript
208
- const DEFAULT_MODEL_ENCODING = "gpt-3.5-turbo";
209
- const DEFAULT_MAX_WEB_SEARCH_TOKENS = 4000;
105
+ ```bash
106
+ bun run --cwd plugins/plugin-web-search build # compile
107
+ bun run --cwd plugins/plugin-web-search dev # watch mode
108
+ bun run --cwd plugins/plugin-web-search lint # biome check
109
+ bun run --cwd plugins/plugin-web-search typecheck # type-check only
210
110
  ```
211
111
 
212
- ### Search Actions
213
-
214
- The plugin includes multiple search action similes:
215
-
216
- - SEARCH_WEB
217
- - INTERNET_SEARCH
218
- - LOOKUP
219
- - QUERY_WEB
220
- - FIND_ONLINE
221
- - And more...
222
-
223
- ## Contributing
224
-
225
- Contributions are welcome! Please see the [CONTRIBUTING.md](CONTRIBUTING.md) file for more information.
226
-
227
- ## Credits
228
-
229
- This plugin integrates with and builds upon several key technologies:
230
-
231
- - [Tavily API](https://tavily.com/): Advanced search and content analysis API
232
- - [js-tiktoken](https://github.com/dqbd/tiktoken): Token counting for API responses
233
- - [Zod](https://github.com/colinhacks/zod): TypeScript-first schema validation
234
-
235
- Special thanks to:
236
-
237
- - The Eliza community for their contributions and feedback
238
-
239
- For more information about the search capabilities and tools:
240
-
241
- - [Tavily API Documentation](https://docs.tavily.com/)
242
- - [Token Management Guide](https://github.com/dqbd/tiktoken#readme)
243
- - [Search API Best Practices](https://docs.tavily.com/docs/guides/best-practices)
112
+ ## Dependencies
244
113
 
245
- ## License
114
+ - [`@elizaos/core`](https://github.com/elizaOS/eliza) — elizaOS runtime interfaces
115
+ - [`@tavily/core`](https://www.npmjs.com/package/@tavily/core) — Tavily search client
246
116
 
247
- This plugin is part of the Eliza project. See the main project repository for license information.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/plugin-web-search",
3
- "version": "1.0.1",
3
+ "version": "2.0.3-beta.5",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -8,42 +8,50 @@
8
8
  "exports": {
9
9
  "./package.json": "./package.json",
10
10
  ".": {
11
+ "eliza-source": {
12
+ "types": "./src/index.ts",
13
+ "import": "./src/index.ts",
14
+ "default": "./src/index.ts"
15
+ },
11
16
  "import": {
12
- "@elizaos/source": "./src/index.ts",
13
17
  "types": "./dist/index.d.ts",
14
18
  "default": "./dist/index.js"
15
19
  }
20
+ },
21
+ "./*.css": "./dist/*.css",
22
+ "./*": {
23
+ "types": "./dist/*.d.ts",
24
+ "eliza-source": {
25
+ "types": "./src/*.ts",
26
+ "import": "./src/*.ts",
27
+ "default": "./src/*.ts"
28
+ },
29
+ "import": "./dist/*.js",
30
+ "default": "./dist/*.js"
16
31
  }
17
32
  },
18
33
  "files": [
19
34
  "dist"
20
35
  ],
21
36
  "dependencies": {
22
- "@tavily/core": "0.5.13"
37
+ "@elizaos/core": "2.0.3-beta.5",
38
+ "@tavily/core": "^0.7.0"
23
39
  },
24
40
  "devDependencies": {
25
- "@elizaos/config": "1.6.5",
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"
41
+ "@biomejs/biome": "2.5.0",
42
+ "tsup": "^8.5.1",
43
+ "typescript": "^6.0.3",
44
+ "vitest": "^4.1.7"
36
45
  },
37
46
  "scripts": {
47
+ "test": "vitest run --config ./vitest.config.ts",
38
48
  "build": "tsup --format esm --dts",
39
49
  "dev": "tsup --format esm --dts --watch",
40
- "lint": "eslint ./src --fix && prettier --write ./src",
41
- "lint:check": "eslint ./src",
42
- "format": "prettier --write ./src",
43
- "format:fix": "prettier --write ./src"
44
- },
45
- "publishConfig": {
46
- "access": "public"
50
+ "lint": "bunx @biomejs/biome check src/",
51
+ "lint:fix": "bunx @biomejs/biome check --write src/",
52
+ "format": "bunx @biomejs/biome format src/",
53
+ "format:fix": "bunx @biomejs/biome format --write src/",
54
+ "typecheck": "tsgo --noEmit -p tsconfig.json"
47
55
  },
48
56
  "agentConfig": {
49
57
  "pluginType": "elizaos:client:1.0.0",
@@ -53,5 +61,9 @@
53
61
  "description": "Tavily API key for accessing news services"
54
62
  }
55
63
  }
56
- }
64
+ },
65
+ "publishConfig": {
66
+ "access": "public"
67
+ },
68
+ "gitHead": "ff6157011c9459670021cc28a6797592a78b8817"
57
69
  }
package/dist/index.d.ts DELETED
@@ -1,67 +0,0 @@
1
- import * as _elizaos_core from '@elizaos/core';
2
- import { Service, IAgentRuntime } from '@elizaos/core';
3
-
4
- interface ITavilyService extends Service {
5
- search(query: string, options?: SearchOptions): Promise<SearchResponse>;
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
- }
44
-
45
- declare class TavilyService extends Service implements ITavilyService {
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 };
package/dist/index.js DELETED
@@ -1,500 +0,0 @@
1
- // src/actions/webSearch.ts
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
22
-
23
- Respond ONLY with the following XML format:
24
- <response>
25
- <reasoning>
26
- Step 1: Identify what information the user is looking for
27
- Step 2: Determine the best search query to find this information
28
- Step 3: Classify the topic based on the subject matter
29
- </reasoning>
30
- <query>the exact query to search</query>
31
- <topic>general or finance</topic>
32
- </response>`;
33
- }
34
- function MaxTokens(data, maxTokens = DEFAULT_MAX_WEB_SEARCH_CHARS) {
35
- return data.length > maxTokens ? data.slice(0, maxTokens) : data;
36
- }
37
- async function extractSearchParams(runtime, message, _state) {
38
- var _a, _b, _c, _d, _e, _f, _g, _h;
39
- const composedState = await runtime.composeState(
40
- message,
41
- ["ACTION_STATE"],
42
- true
43
- );
44
- const stateParams = ((_a = composedState == null ? void 0 : composedState.data) == null ? void 0 : _a.actionParams) || ((_b = composedState == null ? void 0 : composedState.data) == null ? void 0 : _b.webSearch) || ((_c = composedState == null ? void 0 : composedState.data) == null ? void 0 : _c.websearch) || {};
45
- if ((_d = stateParams == null ? void 0 : stateParams.query) == null ? void 0 : _d.trim()) {
46
- logger.info(
47
- { src: "webSearch:extractParams", source: "actionParams" },
48
- "Using params from state"
49
- );
50
- return stateParams;
51
- }
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
- }
104
- var webSearch = {
105
- name: "WEB_SEARCH",
106
- similes: [
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";
429
- tavilyClient;
430
- constructor(runtime) {
431
- super(runtime);
432
- }
433
- static async start(runtime) {
434
- const service = new _TavilyService(runtime);
435
- await service.initialize(runtime);
436
- return service;
437
- }
438
- async initialize(runtime) {
439
- const apiKey = runtime.getSetting("TAVILY_API_KEY");
440
- if (!apiKey) {
441
- throw new Error("TAVILY_API_KEY is not set");
442
- }
443
- this.tavilyClient = tavily({ apiKey });
444
- }
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
- async search(query, options) {
451
- try {
452
- if (!this.tavilyClient) {
453
- throw new Error("TavilyService not initialized");
454
- }
455
- const response = await this.tavilyClient.search(query, {
456
- auto_parameters: options == null ? void 0 : options.auto_parameters,
457
- topic: (options == null ? void 0 : options.topic) ?? "general",
458
- search_depth: (options == null ? void 0 : options.search_depth) ?? "basic",
459
- chunks_per_source: options == null ? void 0 : options.chunks_per_source,
460
- max_results: (options == null ? void 0 : options.max_results) ?? 5,
461
- time_range: options == null ? void 0 : options.time_range,
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
472
- });
473
- return response;
474
- } catch (error) {
475
- logger2.error(
476
- { src: "tavilyService:search", error: error.message },
477
- "Tavily search error"
478
- );
479
- throw error;
480
- }
481
- }
482
- };
483
-
484
- // src/index.ts
485
- var webSearchPlugin = {
486
- name: "webSearch",
487
- description: "Search the web using Tavily",
488
- actions: [webSearch],
489
- evaluators: [],
490
- providers: [],
491
- services: [TavilyService],
492
- clients: [],
493
- adapters: []
494
- };
495
- var index_default = webSearchPlugin;
496
- export {
497
- index_default as default,
498
- webSearchPlugin
499
- };
500
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
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"]}