From 0716819a0efd1c5fce937b9181c143690243d35b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 15 Oct 2025 03:43:55 -0700 Subject: [PATCH] feat(providers): fail gracefully when a provider is misconfigured (#2768) --- lua/avante/llm.lua | 5 ++++- lua/avante/providers/azure.lua | 11 +++++++++-- lua/avante/providers/bedrock.lua | 10 +++++++--- lua/avante/providers/claude.lua | 11 +++++++++-- lua/avante/providers/cohere.lua | 11 ++++++++++- lua/avante/providers/gemini.lua | 7 ++++++- lua/avante/providers/ollama.lua | 13 +++++++++++-- lua/avante/providers/openai.lua | 5 ++++- lua/avante/types.lua | 2 +- 9 files changed, 61 insertions(+), 14 deletions(-) diff --git a/lua/avante/llm.lua b/lua/avante/llm.lua index 54962dc..020eb58 100644 --- a/lua/avante/llm.lua +++ b/lua/avante/llm.lua @@ -548,8 +548,11 @@ function M.curl(opts) if orig_on_stop then return orig_on_stop(stop_opts) end end - ---@type AvanteCurlOutput local spec = provider:parse_curl_args(prompt_opts) + if not spec then + handler_opts.on_stop({ reason = "error", error = "Provider configuration error" }) + return + end ---@type string local current_event_state = nil diff --git a/lua/avante/providers/azure.lua b/lua/avante/providers/azure.lua index b9cb336..7da7612 100644 --- a/lua/avante/providers/azure.lua +++ b/lua/avante/providers/azure.lua @@ -20,6 +20,8 @@ M.api_key_name = "AZURE_OPENAI_API_KEY" -- Inherit from OpenAI class setmetatable(M, { __index = O }) +---@param prompt_opts AvantePromptOptions +---@return AvanteCurlOutput|nil function M:parse_curl_args(prompt_opts) local provider_conf, request_body = P.parse_config(self) local disable_tools = provider_conf.disable_tools or false @@ -29,10 +31,15 @@ function M:parse_curl_args(prompt_opts) } if P.env.require_api_key(provider_conf) then + local api_key = self.parse_api_key() + if not api_key then + Utils.error("Azure: API key is not set. Please set " .. M.api_key_name) + return nil + end if provider_conf.entra then - headers["Authorization"] = "Bearer " .. self.parse_api_key() + headers["Authorization"] = "Bearer " .. api_key else - headers["api-key"] = self.parse_api_key() + headers["api-key"] = api_key end end diff --git a/lua/avante/providers/bedrock.lua b/lua/avante/providers/bedrock.lua index 18e3374..9bb1ef1 100644 --- a/lua/avante/providers/bedrock.lua +++ b/lua/avante/providers/bedrock.lua @@ -125,7 +125,7 @@ function M:parse_response_without_stream(data, event_state, opts) end ---@param prompt_opts AvantePromptOptions ----@return table +---@return AvanteCurlOutput|nil function M:parse_curl_args(prompt_opts) local provider_conf, request_body = P.parse_config(self) @@ -136,9 +136,12 @@ function M:parse_curl_args(prompt_opts) if profile ~= nil and profile ~= "" then ---@diagnostic disable-next-line: undefined-field region = provider_conf.aws_region + if not region or region == "" then + Utils.error("Bedrock: no aws_region specified in bedrock config") + return nil + end local awsCreds = M:get_aws_credentials(region, profile) - if not region or region == "" then error("No aws_region specified in bedrock config") end access_key_id = awsCreds.access_key_id secret_access_key = awsCreds.secret_access_key @@ -153,7 +156,8 @@ function M:parse_curl_args(prompt_opts) region = parts[3] session_token = parts[4] else - error("API key not set correctly") + Utils.error("Bedrock: API key not set correctly") + return nil end end diff --git a/lua/avante/providers/claude.lua b/lua/avante/providers/claude.lua index e17f011..9f476b7 100644 --- a/lua/avante/providers/claude.lua +++ b/lua/avante/providers/claude.lua @@ -315,7 +315,7 @@ function M:parse_response(ctx, data_stream, event_state, opts) end ---@param prompt_opts AvantePromptOptions ----@return table +---@return AvanteCurlOutput|nil function M:parse_curl_args(prompt_opts) local provider_conf, request_body = P.parse_config(self) local disable_tools = provider_conf.disable_tools or false @@ -326,7 +326,14 @@ function M:parse_curl_args(prompt_opts) ["anthropic-beta"] = "prompt-caching-2024-07-31", } - if P.env.require_api_key(provider_conf) then headers["x-api-key"] = self.parse_api_key() end + if P.env.require_api_key(provider_conf) then + local api_key = self.parse_api_key() + if not api_key then + Utils.error("Claude: API key is not set. Please set " .. M.api_key_name) + return nil + end + headers["x-api-key"] = api_key + end local messages = self:parse_messages(prompt_opts) diff --git a/lua/avante/providers/cohere.lua b/lua/avante/providers/cohere.lua index 8a0852a..6cc99c0 100644 --- a/lua/avante/providers/cohere.lua +++ b/lua/avante/providers/cohere.lua @@ -76,6 +76,8 @@ function M:parse_stream_data(ctx, data, opts) end end +---@param prompt_opts AvantePromptOptions +---@return AvanteCurlOutput|nil function M:parse_curl_args(prompt_opts) local provider_conf, request_body = P.parse_config(self) @@ -89,7 +91,14 @@ function M:parse_curl_args(prompt_opts) .. "." .. vim.version().patch, } - if P.env.require_api_key(provider_conf) then headers["Authorization"] = "Bearer " .. self.parse_api_key() end + if P.env.require_api_key(provider_conf) then + local api_key = self.parse_api_key() + if not api_key then + Utils.error("Cohere: API key is not set. Please set " .. M.api_key_name) + return nil + end + headers["Authorization"] = "Bearer " .. api_key + end return { url = Utils.url_join(provider_conf.endpoint, "/chat"), diff --git a/lua/avante/providers/gemini.lua b/lua/avante/providers/gemini.lua index 5f30b1e..0bf525e 100644 --- a/lua/avante/providers/gemini.lua +++ b/lua/avante/providers/gemini.lua @@ -312,11 +312,16 @@ function M:parse_response(ctx, data_stream, _, opts) end end +---@param prompt_opts AvantePromptOptions +---@return AvanteCurlOutput|nil function M:parse_curl_args(prompt_opts) local provider_conf, request_body = Providers.parse_config(self) local api_key = self:parse_api_key() - if api_key == nil then error("Cannot get the gemini api key!") end + if api_key == nil then + Utils.error("Gemini: API key is not set. Please set " .. M.api_key_name) + return nil + end return { url = Utils.url_join( diff --git a/lua/avante/providers/ollama.lua b/lua/avante/providers/ollama.lua index 0c236da..84be40e 100644 --- a/lua/avante/providers/ollama.lua +++ b/lua/avante/providers/ollama.lua @@ -190,12 +190,21 @@ function M:parse_stream_data(ctx, data, opts) end ---@param prompt_opts AvantePromptOptions +---@return AvanteCurlOutput|nil function M:parse_curl_args(prompt_opts) local provider_conf, request_body = Providers.parse_config(self) local keep_alive = provider_conf.keep_alive or "5m" - if not provider_conf.model or provider_conf.model == "" then error("Ollama model must be specified in config") end - if not provider_conf.endpoint then error("Ollama requires endpoint configuration") end + if not provider_conf.model or provider_conf.model == "" then + Utils.error("Ollama: model must be specified in config") + return nil + end + + if not provider_conf.endpoint then + Utils.error("Ollama: endpoint must be specified in config") + return nil + end + local headers = { ["Content-Type"] = "application/json", ["Accept"] = "application/json", diff --git a/lua/avante/providers/openai.lua b/lua/avante/providers/openai.lua index 8d64803..18c6de2 100644 --- a/lua/avante/providers/openai.lua +++ b/lua/avante/providers/openai.lua @@ -504,6 +504,8 @@ function M:parse_response_without_stream(data, _, opts) end end +---@param prompt_opts AvantePromptOptions +---@return AvanteCurlOutput|nil function M:parse_curl_args(prompt_opts) local provider_conf, request_body = Providers.parse_config(self) local disable_tools = provider_conf.disable_tools or false @@ -515,7 +517,8 @@ function M:parse_curl_args(prompt_opts) if Providers.env.require_api_key(provider_conf) then local api_key = self.parse_api_key() if api_key == nil then - error(Config.provider .. " API key is not set, please set it in your environment variable or config file") + Utils.error(Config.provider .. ": API key is not set, please set it in your environment variable or config file") + return nil end headers["Authorization"] = "Bearer " .. api_key end diff --git a/lua/avante/types.lua b/lua/avante/types.lua index 4b37c50..14d2784 100644 --- a/lua/avante/types.lua +++ b/lua/avante/types.lua @@ -230,7 +230,7 @@ vim.g.avante_login = vim.g.avante_login ---@alias AvanteMessagesParser fun(self: AvanteProviderFunctor, opts: AvantePromptOptions): AvanteChatMessage[] --- ---@class AvanteCurlOutput: {url: string, proxy: string, insecure: boolean, body: table | string, headers: table, rawArgs: string[] | nil} ----@alias AvanteCurlArgsParser fun(self: AvanteProviderFunctor, prompt_opts: AvantePromptOptions): AvanteCurlOutput +---@alias AvanteCurlArgsParser fun(self: AvanteProviderFunctor, prompt_opts: AvantePromptOptions): (AvanteCurlOutput | nil) --- ---@alias AvanteResponseParser fun(self: AvanteProviderFunctor, ctx: any, data_stream: string, event_state: string, opts: AvanteHandlerOptions): nil ---