Skip to content

Commit 5d94e98

Browse files
committed
Merge branch 'tcm-tavily' of http://github.com/ai16z/eliza into HEAD
2 parents 1aeaab2 + 529328f commit 5d94e98

File tree

9 files changed

+295
-4
lines changed

9 files changed

+295
-4
lines changed

packages/client-direct/src/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ export class DirectClient {
239239
);
240240

241241
if (message) {
242-
res.json([message, response]);
242+
res.json([response, message]);
243243
} else {
244244
res.json([response]);
245245
}

packages/core/src/generation.ts

+34
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
ModelClass,
3232
ModelProviderName,
3333
ServiceType,
34+
SearchResponse,
3435
} from "./types.ts";
3536
import { fal, } from "@fal-ai/client";
3637

@@ -972,6 +973,39 @@ export const generateCaption = async (
972973
description: resp.description.trim(),
973974
};
974975
};
976+
977+
export const generateWebSearch = async (
978+
query: string,
979+
runtime: IAgentRuntime
980+
): Promise<SearchResponse> => {
981+
const apiUrl = "https://api.tavily.com/search";
982+
const apiKey = runtime.getSetting("TAVILY_API_KEY");
983+
984+
try {
985+
const response = await fetch(apiUrl, {
986+
method: "POST",
987+
headers: {
988+
"Content-Type": "application/json",
989+
},
990+
body: JSON.stringify({
991+
api_key: apiKey,
992+
query,
993+
include_answer: true,
994+
}),
995+
});
996+
997+
if (!response.ok) {
998+
throw new elizaLogger.error(
999+
`HTTP error! status: ${response.status}`
1000+
);
1001+
}
1002+
1003+
const data: SearchResponse = await response.json();
1004+
return data;
1005+
} catch (error) {
1006+
elizaLogger.error("Error:", error);
1007+
}
1008+
};
9751009
/**
9761010
* Configuration options for generating objects with a model.
9771011
*/

packages/core/src/types.ts

+17
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,23 @@ export interface IPdfService extends Service {
11001100
convertPdfToText(pdfBuffer: Buffer): Promise<string>;
11011101
}
11021102

1103+
export type SearchResult = {
1104+
title: string;
1105+
url: string;
1106+
content: string;
1107+
score: number;
1108+
raw_content: string | null;
1109+
};
1110+
1111+
export type SearchResponse = {
1112+
query: string;
1113+
follow_up_questions: string[] | null;
1114+
answer: string | null;
1115+
images: string[];
1116+
results: SearchResult[];
1117+
response_time: number;
1118+
};
1119+
11031120
export enum ServiceType {
11041121
IMAGE_DESCRIPTION = "image_description",
11051122
TRANSCRIPTION = "transcription",

packages/plugin-web-search/.npmignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
*
2+
3+
!dist/**
4+
!package.json
5+
!readme.md
6+
!tsup.config.ts
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"name": "@ai16z/plugin-web-search",
3+
"version": "0.1.3",
4+
"main": "dist/index.js",
5+
"type": "module",
6+
"types": "dist/index.d.ts",
7+
"dependencies": {
8+
"@ai16z/eliza": "workspace:*",
9+
"tsup": "^8.3.5"
10+
},
11+
"scripts": {
12+
"build": "tsup --format esm --dts"
13+
},
14+
"peerDependencies": {
15+
"whatwg-url": "7.1.0"
16+
}
17+
}
+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import { elizaLogger } from "@ai16z/eliza";
2+
import {
3+
Action,
4+
HandlerCallback,
5+
IAgentRuntime,
6+
Memory,
7+
Plugin,
8+
State,
9+
} from "@ai16z/eliza";
10+
import { generateWebSearch } from "@ai16z/eliza";
11+
12+
import { SearchResult } from "@ai16z/eliza";
13+
14+
const webSearch: Action = {
15+
name: "WEB_SEARCH",
16+
similes: [
17+
"SEARCH_WEB",
18+
"INTERNET_SEARCH",
19+
"LOOKUP",
20+
"QUERY_WEB",
21+
"FIND_ONLINE",
22+
"SEARCH_ENGINE",
23+
"WEB_LOOKUP",
24+
"ONLINE_SEARCH",
25+
"FIND_INFORMATION",
26+
],
27+
description:
28+
"Perform a web search to find information related to the message.",
29+
validate: async (runtime: IAgentRuntime, message: Memory) => {
30+
const tavilyApiKeyOk = !!runtime.getSetting("TAVILY_API_KEY");
31+
32+
return tavilyApiKeyOk;
33+
},
34+
handler: async (
35+
runtime: IAgentRuntime,
36+
message: Memory,
37+
state: State,
38+
options: any,
39+
callback: HandlerCallback
40+
) => {
41+
elizaLogger.log("Composing state for message:", message);
42+
state = (await runtime.composeState(message)) as State;
43+
const userId = runtime.agentId;
44+
elizaLogger.log("User ID:", userId);
45+
46+
const webSearchPrompt = message.content.text;
47+
elizaLogger.log("web search prompt received:", webSearchPrompt);
48+
49+
elizaLogger.log("Generating image with prompt:", webSearchPrompt);
50+
const searchResponse = await generateWebSearch(
51+
webSearchPrompt,
52+
runtime
53+
);
54+
55+
if (searchResponse && searchResponse.results.length) {
56+
const responseList = searchResponse.answer
57+
? `${searchResponse.answer}${
58+
Array.isArray(searchResponse.results) &&
59+
searchResponse.results.length > 0
60+
? `\n\nFor more details, you can check out these resources:\n${searchResponse.results
61+
.map(
62+
(result: SearchResult, index: number) =>
63+
`${index + 1}. [${result.title}](${result.url})`
64+
)
65+
.join("\n")}`
66+
: ""
67+
}`
68+
: "";
69+
70+
callback({
71+
text: responseList,
72+
});
73+
} else {
74+
elizaLogger.error("search failed or returned no data.");
75+
}
76+
},
77+
examples: [
78+
[
79+
{
80+
user: "{{user1}}",
81+
content: {
82+
text: "Find the latest news about SpaceX launches.",
83+
},
84+
},
85+
{
86+
user: "{{agentName}}",
87+
content: {
88+
text: "Here is the latest news about SpaceX launches:",
89+
action: "WEB_SEARCH",
90+
},
91+
},
92+
],
93+
[
94+
{
95+
user: "{{user1}}",
96+
content: {
97+
text: "Can you find details about the iPhone 16 release?",
98+
},
99+
},
100+
{
101+
user: "{{agentName}}",
102+
content: {
103+
text: "Here are the details I found about the iPhone 16 release:",
104+
action: "WEB_SEARCH",
105+
},
106+
},
107+
],
108+
[
109+
{
110+
user: "{{user1}}",
111+
content: {
112+
text: "What is the schedule for the next FIFA World Cup?",
113+
},
114+
},
115+
{
116+
user: "{{agentName}}",
117+
content: {
118+
text: "Here is the schedule for the next FIFA World Cup:",
119+
action: "WEB_SEARCH",
120+
},
121+
},
122+
],
123+
[
124+
{
125+
user: "{{user1}}",
126+
content: { text: "Check the latest stock price of Tesla." },
127+
},
128+
{
129+
user: "{{agentName}}",
130+
content: {
131+
text: "Here is the latest stock price of Tesla I found:",
132+
action: "WEB_SEARCH",
133+
},
134+
},
135+
],
136+
[
137+
{
138+
user: "{{user1}}",
139+
content: {
140+
text: "What are the current trending movies in the US?",
141+
},
142+
},
143+
{
144+
user: "{{agentName}}",
145+
content: {
146+
text: "Here are the current trending movies in the US:",
147+
action: "WEB_SEARCH",
148+
},
149+
},
150+
],
151+
[
152+
{
153+
user: "{{user1}}",
154+
content: {
155+
text: "What is the latest score in the NBA finals?",
156+
},
157+
},
158+
{
159+
user: "{{agentName}}",
160+
content: {
161+
text: "Here is the latest score from the NBA finals:",
162+
action: "WEB_SEARCH",
163+
},
164+
},
165+
],
166+
[
167+
{
168+
user: "{{user1}}",
169+
content: { text: "When is the next Apple keynote event?" },
170+
},
171+
{
172+
user: "{{agentName}}",
173+
content: {
174+
text: "Here is the information about the next Apple keynote event:",
175+
action: "WEB_SEARCH",
176+
},
177+
},
178+
],
179+
],
180+
} as Action;
181+
182+
export const webSearchPlugin: Plugin = {
183+
name: "webSearch",
184+
description: "Search web",
185+
actions: [webSearch],
186+
evaluators: [],
187+
providers: [],
188+
};
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "dist",
5+
"rootDir": "src",
6+
"types": ["node"]
7+
},
8+
"include": ["src/**/*.ts"]
9+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { defineConfig } from "tsup";
2+
3+
export default defineConfig({
4+
entry: ["src/index.ts"],
5+
outDir: "dist",
6+
sourcemap: true,
7+
clean: true,
8+
format: ["esm"], // Ensure you're targeting CommonJS
9+
external: [
10+
"dotenv", // Externalize dotenv to prevent bundling
11+
"fs", // Externalize fs to use Node.js built-in module
12+
"path", // Externalize other built-ins if necessary
13+
"@reflink/reflink",
14+
"@node-llama-cpp",
15+
"https",
16+
"http",
17+
"agentkeepalive",
18+
"zod",
19+
// Add other modules you want to externalize
20+
],
21+
});

scripts/build.sh

+2-3
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,15 @@ PACKAGES=(
3535
"plugin-bootstrap"
3636
"plugin-image-generation"
3737
"plugin-coinbase"
38-
"plugin-node"
39-
"plugin-bootstrap"
4038
"plugin-evm"
41-
"plugin-image-generation"
4239
"plugin-tee"
4340
"client-auto"
4441
"client-direct"
4542
"client-discord"
4643
"client-telegram"
4744
"client-twitter"
45+
"client-whatsapp"
46+
"plugin-web-search"
4847
)
4948

5049
# Build packages in specified order

0 commit comments

Comments
 (0)