From f27b8b26393500bdc97824b37faa9bfbd7626670 Mon Sep 17 00:00:00 2001 From: IqbalHossainEmon Date: Sat, 4 Jul 2026 04:13:17 +0600 Subject: [PATCH 1/2] fix: add fallbacks for missing authorization headers in provider config --- README.md | 2 +- lua/CopilotChat/client.lua | 8 +++++++- lua/CopilotChat/completion.lua | 2 +- lua/CopilotChat/config/providers.lua | 24 +++++++++++++++++++++--- lua/CopilotChat/init.lua | 4 +++- 5 files changed, 33 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 434cae00..dbc29b03 100644 --- a/README.md +++ b/README.md @@ -452,7 +452,7 @@ Add custom AI providers: get_models?(headers: table): table, -- Optional: Resolve a user-facing model id to a provider model id - resolve_model?(headers: table, model: string): string, + resolve_model?(headers: table, model: string): string, table, } ``` diff --git a/lua/CopilotChat/client.lua b/lua/CopilotChat/client.lua index 7bbc65d8..180cd8c2 100644 --- a/lua/CopilotChat/client.lua +++ b/lua/CopilotChat/client.lua @@ -319,9 +319,11 @@ function Client:ask(opts) error('Provider not found: ' .. provider_name) end + local resolved_headers = nil if provider.resolve_model then local headers = self:authenticate(provider_name) - local resolved_model = provider.resolve_model(headers, opts.model) + local resolved_model, extra_headers = provider.resolve_model(headers, opts.model) + resolved_headers = extra_headers opts.model = resolved_model model_config = models[opts.model] if not model_config then @@ -538,6 +540,10 @@ function Client:ask(opts) local headers = self:authenticate(provider_name) + if resolved_headers then + headers = vim.tbl_extend('force', headers, resolved_headers) + end + local request, extra_headers = provider.prepare_input(generate_ask_request(opts.system_prompt, history, generated_messages), options) diff --git a/lua/CopilotChat/completion.lua b/lua/CopilotChat/completion.lua index fdb509de..62976d03 100644 --- a/lua/CopilotChat/completion.lua +++ b/lua/CopilotChat/completion.lua @@ -139,7 +139,7 @@ function M.complete(without_input) local row, col = unpack(vim.api.nvim_win_get_cursor(win)) local prefix, cmp_start = unpack(vim.fn.matchstrpos(line:sub(1, col), info.pattern)) - if not prefix then + if cmp_start == -1 then return end diff --git a/lua/CopilotChat/config/providers.lua b/lua/CopilotChat/config/providers.lua index ea5eea71..70e0bb70 100644 --- a/lua/CopilotChat/config/providers.lua +++ b/lua/CopilotChat/config/providers.lua @@ -530,7 +530,7 @@ end ---@field get_headers nil|fun():table,number? ---@field get_info nil|fun(headers:table):string[] ---@field get_models nil|fun(headers:table):table ----@field resolve_model nil|fun(headers:table, model: string):string +---@field resolve_model nil|fun(headers:table, model: string):string, table? ---@field prepare_input nil|fun(inputs:CopilotChat.client.Message[], opts:CopilotChat.config.providers.Options):table,table? ---@field prepare_output nil|fun(output:table, opts:CopilotChat.config.providers.Options):CopilotChat.config.providers.Output ---@field get_url nil|fun(opts:CopilotChat.config.providers.Options):string @@ -626,19 +626,37 @@ M.copilot = { -- Build request headers without our internal routing header. local request_headers = vim.tbl_extend('force', headers, { ['x-copilot-base-url'] = nil }) + local auth = headers.Authorization or headers.authorization or '' + local sku = auth:match('sku=([^;]+)') or 'free' + local response, err = curl.get(base_url .. '/models', { json_response = true, headers = request_headers, }) if err then + print(debug.traceback('get_models error: ' .. vim.inspect(err))) error(err) end + local function sku_matches_restricted_to(sku, restricted_to) + if not restricted_to or vim.tbl_isempty(restricted_to) then + return true + end + + for _, tier in ipairs(restricted_to) do + if sku:find(tier, 1, true) then + return true + end + end + + return false + end + local models = vim .iter(response.body.data) :filter(function(model) - return model.capabilities.type == 'chat' and model.model_picker_enabled + return model.capabilities.type == 'chat' and sku_matches_restricted_to(sku, model.restricted_to) end) :map(function(model) local supported_endpoints = model.supported_endpoints or {} @@ -714,7 +732,7 @@ M.copilot = { error(err) end - return response.body.selected_model + return response.body.selected_model, { ['Copilot-Session-Token'] = response.body.session_token } end, prepare_input = function(inputs, opts) diff --git a/lua/CopilotChat/init.lua b/lua/CopilotChat/init.lua index feba3754..92f59b9a 100644 --- a/lua/CopilotChat/init.lua +++ b/lua/CopilotChat/init.lua @@ -557,6 +557,7 @@ function M.ask(prompt, config) end local response = ask_response.message + local effective_model = response.model or selected_model local token_count = ask_response.token_count local token_max_count = ask_response.token_max_count @@ -632,7 +633,7 @@ function M.ask(prompt, config) resources = resolved_resources, tools = selected_tools, system_prompt = system_prompt, - model = selected_model, + model = effective_model, temperature = config.temperature, on_progress = vim.schedule_wrap(function(message) if not config.headless then @@ -643,6 +644,7 @@ function M.ask(prompt, config) if continue_response then local continue_message = continue_response.message + effective_model = continue_message.model or effective_model continue_message.content = vim.trim(continue_message.content) if utils.empty(continue_message.content) then continue_message.content = '' From 64e1e035d4ccc7397dc0c8946664632a7a47429e Mon Sep 17 00:00:00 2001 From: IqbalHossainEmon Date: Sat, 4 Jul 2026 08:59:49 +0600 Subject: [PATCH 2/2] chore: apply review feedback --- .github/workflows/pullfrog.yml | 7 +++---- README.md | 2 +- lua/CopilotChat/config/providers.lua | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/pullfrog.yml b/.github/workflows/pullfrog.yml index 6516ae2e..d5030252 100644 --- a/.github/workflows/pullfrog.yml +++ b/.github/workflows/pullfrog.yml @@ -34,22 +34,21 @@ jobs: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} - GOOGLE_GENERATIVE_AI_API_KEY: - ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }} + GOOGLE_GENERATIVE_AI_API_KEY: ${{ secrets.GOOGLE_GENERATIVE_AI_API_KEY }} GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} XAI_API_KEY: ${{ secrets.XAI_API_KEY }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} MOONSHOT_API_KEY: ${{ secrets.MOONSHOT_API_KEY }} OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} - + # for Amazon Bedrock (https://docs.pullfrog.com/bedrock) # AWS_BEARER_TOKEN_BEDROCK: ${{ secrets.AWS_BEARER_TOKEN_BEDROCK }} # AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} # AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} # AWS_REGION: us-east-1 # BEDROCK_MODEL_ID: - + # for Google Vertex AI (https://docs.pullfrog.com/vertex) # VERTEX_SERVICE_ACCOUNT_JSON: ${{ secrets.VERTEX_SERVICE_ACCOUNT_JSON }} # GOOGLE_CLOUD_PROJECT: my-project diff --git a/README.md b/README.md index dbc29b03..00684da4 100644 --- a/README.md +++ b/README.md @@ -452,7 +452,7 @@ Add custom AI providers: get_models?(headers: table): table, -- Optional: Resolve a user-facing model id to a provider model id - resolve_model?(headers: table, model: string): string, table, + resolve_model?(headers: table, model: string): string, table?, } ``` diff --git a/lua/CopilotChat/config/providers.lua b/lua/CopilotChat/config/providers.lua index 70e0bb70..2f179f50 100644 --- a/lua/CopilotChat/config/providers.lua +++ b/lua/CopilotChat/config/providers.lua @@ -635,7 +635,6 @@ M.copilot = { }) if err then - print(debug.traceback('get_models error: ' .. vim.inspect(err))) error(err) end