Skip to content

Commit 152d0c6

Browse files
authored
feat: serpapi (#1198)
1 parent 402c7f9 commit 152d0c6

File tree

3 files changed

+99
-13
lines changed

3 files changed

+99
-13
lines changed

README.md

+29
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,35 @@ Avante provides a set of default providers, but users can also create their own
513513
514514
For more information, see [Custom Providers](https://github.com/yetone/avante.nvim/wiki/Custom-providers)
515515
516+
## Web Search Engines
517+
518+
Avante's tools include some web search engines, currently support [tavily](https://tavily.com/) and [serpapi](https://serpapi.com/). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`:
519+
520+
```lua
521+
web_search_engine = {
522+
provider = "tavily", -- tavily or serpapi
523+
}
524+
```
525+
526+
You need to set the environment variable `TAVILY_API_KEY` or `SERPAPI_API_KEY` to use tavily or serpapi.
527+
528+
## Disable Tools
529+
530+
Avante enables tools by default, but some LLM models do not support tools. You can disable tools by setting `disable_tools = true` for the provider. For example:
531+
532+
```lua
533+
{
534+
claude = {
535+
endpoint = "https://api.anthropic.com",
536+
model = "claude-3-5-sonnet-20241022",
537+
timeout = 30000, -- Timeout in milliseconds
538+
temperature = 0,
539+
max_tokens = 4096,
540+
disable_tools = true, -- disable tools!
541+
},
542+
}
543+
```
544+
516545
## Custom prompts
517546

518547
By default, `avante.nvim` provides three different modes to interact with: `planning`, `editing`, and `suggesting`, followed with three different prompts per mode.

lua/avante/config.lua

+40-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
---NOTE: user will be merged with defaults and
22
---we add a default var_accessor for this table to config values.
33

4+
---@alias WebSearchEngineProviderResponseBodyFormatter fun(body: table): (string, string?)
5+
46
local Utils = require("avante.utils")
57

68
---@class avante.CoreConfig: avante.Config
@@ -22,10 +24,44 @@ M._defaults = {
2224
tokenizer = "tiktoken",
2325
web_search_engine = {
2426
provider = "tavily",
25-
api_key_name = "TAVILY_API_KEY",
26-
provider_opts = {
27-
time_range = "d",
28-
include_answer = "basic",
27+
providers = {
28+
tavily = {
29+
api_key_name = "TAVILY_API_KEY",
30+
extra_request_body = {
31+
time_range = "d",
32+
include_answer = "basic",
33+
},
34+
---@type WebSearchEngineProviderResponseBodyFormatter
35+
format_response_body = function(body) return body.anwser, nil end,
36+
},
37+
serpapi = {
38+
api_key_name = "SERPAPI_API_KEY",
39+
extra_request_body = {
40+
engine = "google",
41+
google_domain = "google.com",
42+
},
43+
---@type WebSearchEngineProviderResponseBodyFormatter
44+
format_response_body = function(body)
45+
if body.answer_box ~= nil then return body.answer_box.result, nil end
46+
if body.organic_results ~= nil then
47+
local jsn = vim
48+
.iter(body.organic_results)
49+
:map(
50+
function(result)
51+
return {
52+
title = result.title,
53+
link = result.link,
54+
snippet = result.snippet,
55+
}
56+
end
57+
)
58+
:totable()
59+
if #jsn > 5 then jsn = vim.list_slice(jsn, 1, 5) end
60+
return vim.json.encode(jsn), nil
61+
end
62+
return "", nil
63+
end,
64+
},
2965
},
3066
},
3167
---@type AvanteSupportedProvider

lua/avante/llm_tools.lua

+30-9
Original file line numberDiff line numberDiff line change
@@ -285,26 +285,47 @@ end
285285
---@return string|nil result
286286
---@return string|nil error
287287
function M.web_search(opts, on_log)
288+
local provider_type = Config.web_search_engine.provider
289+
if provider_type == nil then return nil, "Search engine provider is not set" end
290+
if on_log then on_log("provider: " .. provider_type) end
288291
if on_log then on_log("query: " .. opts.query) end
289-
local search_engine = Config.web_search_engine
290-
if search_engine.provider == "tavily" then
291-
if search_engine.api_key_name == "" then return nil, "No API key provided" end
292-
local api_key = os.getenv(search_engine.api_key_name)
293-
if api_key == nil or api_key == "" then
294-
return nil, "Environment variable " .. search_engine.api_key_name .. " is not set"
295-
end
292+
local search_engine = Config.web_search_engine.providers[provider_type]
293+
if search_engine == nil then return nil, "No search engine found: " .. provider_type end
294+
if search_engine.api_key_name == "" then return nil, "No API key provided" end
295+
local api_key = os.getenv(search_engine.api_key_name)
296+
if api_key == nil or api_key == "" then
297+
return nil, "Environment variable " .. search_engine.api_key_name .. " is not set"
298+
end
299+
if provider_type == "tavily" then
296300
local resp = curl.post("https://api.tavily.com/search", {
297301
headers = {
298302
["Content-Type"] = "application/json",
299303
["Authorization"] = "Bearer " .. api_key,
300304
},
301305
body = vim.json.encode(vim.tbl_deep_extend("force", {
302306
query = opts.query,
303-
}, search_engine.provider_opts)),
307+
}, search_engine.extra_request_body)),
308+
})
309+
if resp.status ~= 200 then return nil, "Error: " .. resp.body end
310+
local jsn = vim.json.decode(resp.body)
311+
return search_engine.format_response_body(jsn)
312+
elseif provider_type == "serpapi" then
313+
local query_params = vim.tbl_deep_extend("force", {
314+
api_key = api_key,
315+
q = opts.query,
316+
}, search_engine.extra_request_body)
317+
local query_string = ""
318+
for key, value in pairs(query_params) do
319+
query_string = query_string .. key .. "=" .. vim.uri_encode(value) .. "&"
320+
end
321+
local resp = curl.get("https://serpapi.com/search?" .. query_string, {
322+
headers = {
323+
["Content-Type"] = "application/json",
324+
},
304325
})
305326
if resp.status ~= 200 then return nil, "Error: " .. resp.body end
306327
local jsn = vim.json.decode(resp.body)
307-
return jsn.anwser, nil
328+
return search_engine.format_response_body(jsn)
308329
end
309330
end
310331

0 commit comments

Comments
 (0)