@elizaos/plugin-web-search 2.0.0-beta.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 +21 -0
- package/README.md +67 -198
- package/package.json +34 -12
- package/dist/index.d.ts +0 -7
- package/dist/index.js +0 -197
- package/dist/index.js.map +0 -1
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
|
-
|
|
3
|
+
Adds live web search to an Eliza agent via the [Tavily](https://tavily.com/) API.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## What it does
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
18
|
-
bun 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
|
-
##
|
|
19
|
+
## Installation
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
Add the package to your agent:
|
|
24
22
|
|
|
25
|
-
```
|
|
26
|
-
|
|
23
|
+
```bash
|
|
24
|
+
bun add @elizaos/plugin-web-search
|
|
27
25
|
```
|
|
28
26
|
|
|
29
|
-
|
|
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
|
-
// ...
|
|
34
|
+
// ...
|
|
39
35
|
};
|
|
40
36
|
```
|
|
41
37
|
|
|
42
|
-
|
|
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
|
-
|
|
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
|
-
|
|
44
|
+
Set the key in your environment or agent settings:
|
|
69
45
|
|
|
70
|
-
```
|
|
71
|
-
|
|
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
|
-
|
|
50
|
+
## Calling the service from another plugin
|
|
86
51
|
|
|
87
52
|
```typescript
|
|
88
|
-
|
|
89
|
-
|
|
53
|
+
import { ServiceType } from "@elizaos/core";
|
|
54
|
+
import type { IWebSearchService } from "@elizaos/core";
|
|
90
55
|
|
|
91
|
-
|
|
92
|
-
const response = MaxTokens(searchResult, DEFAULT_MAX_WEB_SEARCH_TOKENS);
|
|
93
|
-
```
|
|
56
|
+
const svc = runtime.getService<IWebSearchService>(ServiceType.WEB_SEARCH);
|
|
94
57
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
-
|
|
65
|
+
// News search
|
|
66
|
+
const news = await svc.searchNews("AI regulation", { freshness: "week" });
|
|
104
67
|
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
bun 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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
103
|
+
## Development
|
|
206
104
|
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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": "2.0.
|
|
3
|
+
"version": "2.0.3-beta.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -8,32 +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
17
|
"types": "./dist/index.d.ts",
|
|
13
18
|
"default": "./dist/index.js"
|
|
14
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"
|
|
15
31
|
}
|
|
16
32
|
},
|
|
17
33
|
"files": [
|
|
18
34
|
"dist"
|
|
19
35
|
],
|
|
20
36
|
"dependencies": {
|
|
21
|
-
"@elizaos/core": "2.0.
|
|
22
|
-
"@tavily/core": "^0.7.0"
|
|
23
|
-
"js-tiktoken": "1.0.21",
|
|
24
|
-
"tsup": "^8.5.1"
|
|
37
|
+
"@elizaos/core": "2.0.3-beta.5",
|
|
38
|
+
"@tavily/core": "^0.7.0"
|
|
25
39
|
},
|
|
26
40
|
"devDependencies": {
|
|
27
|
-
"@biomejs/biome": "2.
|
|
41
|
+
"@biomejs/biome": "2.5.0",
|
|
42
|
+
"tsup": "^8.5.1",
|
|
43
|
+
"typescript": "^6.0.3",
|
|
44
|
+
"vitest": "^4.1.7"
|
|
28
45
|
},
|
|
29
46
|
"scripts": {
|
|
47
|
+
"test": "vitest run --config ./vitest.config.ts",
|
|
30
48
|
"build": "tsup --format esm --dts",
|
|
31
49
|
"dev": "tsup --format esm --dts --watch",
|
|
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": "
|
|
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"
|
|
37
55
|
},
|
|
38
56
|
"agentConfig": {
|
|
39
57
|
"pluginType": "elizaos:client:1.0.0",
|
|
@@ -43,5 +61,9 @@
|
|
|
43
61
|
"description": "Tavily API key for accessing news services"
|
|
44
62
|
}
|
|
45
63
|
}
|
|
46
|
-
}
|
|
64
|
+
},
|
|
65
|
+
"publishConfig": {
|
|
66
|
+
"access": "public"
|
|
67
|
+
},
|
|
68
|
+
"gitHead": "ff6157011c9459670021cc28a6797592a78b8817"
|
|
47
69
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { SearchCategoryRegistration, Plugin, IAgentRuntime } from '@elizaos/core';
|
|
2
|
-
|
|
3
|
-
declare const WEB_SEARCH_CATEGORY: SearchCategoryRegistration;
|
|
4
|
-
declare function registerWebSearchCategory(runtime: IAgentRuntime): void;
|
|
5
|
-
declare const webSearchPlugin: Plugin;
|
|
6
|
-
|
|
7
|
-
export { WEB_SEARCH_CATEGORY, webSearchPlugin as default, registerWebSearchCategory, webSearchPlugin };
|
package/dist/index.js
DELETED
|
@@ -1,197 +0,0 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
import { ServiceType as ServiceType2 } from "@elizaos/core";
|
|
3
|
-
|
|
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;
|
|
11
|
-
}
|
|
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
|
-
};
|
|
35
|
-
}
|
|
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;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
var WebSearchService = class _WebSearchService extends IWebSearchService {
|
|
49
|
-
static serviceType = ServiceType.WEB_SEARCH;
|
|
50
|
-
capabilityDescription = "Web search and content discovery capabilities";
|
|
51
|
-
tavilyClient;
|
|
52
|
-
static async start(runtime) {
|
|
53
|
-
const service = new _WebSearchService(runtime);
|
|
54
|
-
await service.initialize(runtime);
|
|
55
|
-
return service;
|
|
56
|
-
}
|
|
57
|
-
async stop() {
|
|
58
|
-
}
|
|
59
|
-
async initialize(runtime) {
|
|
60
|
-
const apiKey = runtime.getSetting("TAVILY_API_KEY");
|
|
61
|
-
if (typeof apiKey !== "string" || apiKey.length === 0) {
|
|
62
|
-
throw new Error("TAVILY_API_KEY is not set");
|
|
63
|
-
}
|
|
64
|
-
this.tavilyClient = tavily({ apiKey });
|
|
65
|
-
}
|
|
66
|
-
async search(query, options) {
|
|
67
|
-
try {
|
|
68
|
-
const response = await this.tavilyClient.search(query, {
|
|
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
|
|
75
|
-
});
|
|
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;
|
|
81
|
-
}
|
|
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
|
-
}
|
|
129
|
-
};
|
|
130
|
-
|
|
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
|
-
}
|
|
180
|
-
var webSearchPlugin = {
|
|
181
|
-
name: "webSearch",
|
|
182
|
-
description: "Search the web and get news",
|
|
183
|
-
init: async (_config, runtime) => {
|
|
184
|
-
registerWebSearchCategory(runtime);
|
|
185
|
-
},
|
|
186
|
-
actions: [],
|
|
187
|
-
providers: [],
|
|
188
|
-
services: [WebSearchService]
|
|
189
|
-
};
|
|
190
|
-
var index_default = webSearchPlugin;
|
|
191
|
-
export {
|
|
192
|
-
WEB_SEARCH_CATEGORY,
|
|
193
|
-
index_default as default,
|
|
194
|
-
registerWebSearchCategory,
|
|
195
|
-
webSearchPlugin
|
|
196
|
-
};
|
|
197
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
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"]}
|