diff --git a/README.md b/README.md index 54da2b4..085f214 100644 --- a/README.md +++ b/README.md @@ -620,16 +620,25 @@ Therefore, I have adopted Cursor’s method to implement planning applying. For ## Web Search Engines -Avante's tools include some web search engines, currently support [tavily](https://tavily.com/), [serpapi](https://serpapi.com/), [searchapi](https://www.searchapi.io/) and google's [programmable search engine](https://developers.google.com/custom-search/v1/overview). The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`: +Avante's tools include some web search engines, currently support: + +- [tavily](https://tavily.com/) +- [serpapi](https://serpapi.com/) +- [searchapi](https://www.searchapi.io/) +- google's [programmable search engine](https://developers.google.com/custom-search/v1/overview) +- [kagi](https://help.kagi.com/kagi/api/search.html) + +The default is tavily, and can be changed through configuring `Config.web_search_engine.provider`: ```lua web_search_engine = { - provider = "tavily", -- tavily, serpapi, searchapi or google + provider = "tavily", -- tavily, serpapi, searchapi, google or kagi } ``` You need to set the environment variable `TAVILY_API_KEY` , `SERPAPI_API_KEY`, `SEARCHAPI_API_KEY` to use tavily or serpapi or searchapi. To use google, set the `GOOGLE_SEARCH_API_KEY` as the [API key](https://developers.google.com/custom-search/v1/overview), and `GOOGLE_SEARCH_ENGINE_ID` as the [search engine](https://programmablesearchengine.google.com) ID. +To use kagi, set the `KAGI_API_KEY` as the [API Token](https://kagi.com/settings?p=api). ## Disable Tools diff --git a/lua/avante/config.lua b/lua/avante/config.lua index 1fe9f60..66cf756 100644 --- a/lua/avante/config.lua +++ b/lua/avante/config.lua @@ -125,6 +125,32 @@ M._defaults = { return "", nil end, }, + kagi = { + api_key_name = "KAGI_API_KEY", + extra_request_body = { + limit = "10", + }, + ---@type WebSearchEngineProviderResponseBodyFormatter + format_response_body = function(body) + if body.data ~= nil then + local jsn = vim + .iter(body.data) + :map( + function(result) + return { + title = result.title, + url = result.url, + snippet = result.snippet, + } + end + ) + :take(10) + :totable() + return vim.json.encode(jsn), nil + end + return "", nil + end, + }, }, }, ---@type AvanteSupportedProvider diff --git a/lua/avante/llm_tools.lua b/lua/avante/llm_tools.lua index d60c917..a37b953 100644 --- a/lua/avante/llm_tools.lua +++ b/lua/avante/llm_tools.lua @@ -379,6 +379,23 @@ function M.web_search(opts, on_log) if resp.status ~= 200 then return nil, "Error: " .. resp.body end local jsn = vim.json.decode(resp.body) return search_engine.format_response_body(jsn) + elseif provider_type == "kagi" then + local query_params = vim.tbl_deep_extend("force", { + q = opts.query, + }, search_engine.extra_request_body) + local query_string = "" + for key, value in pairs(query_params) do + query_string = query_string .. key .. "=" .. vim.uri_encode(value) .. "&" + end + local resp = curl.get("https://kagi.com/api/v0/search?" .. query_string, { + headers = { + ["Authorization"] = "Bot " .. api_key, + ["Content-Type"] = "application/json", + }, + }) + if resp.status ~= 200 then return nil, "Error: " .. resp.body end + local jsn = vim.json.decode(resp.body) + return search_engine.format_response_body(jsn) end end