From f53aa4001cfe3f4133cd316f32cae2b7a099f420 Mon Sep 17 00:00:00 2001 From: Michael Rosenfeld Date: Thu, 25 Jun 2026 16:46:04 -0400 Subject: [PATCH 1/3] feat(client): support extra headers from model resolution - Allow providers to return extra headers from resolve_model callback - Merge resolved headers into request authentication - Handle 'auto' model fallback when resolution fails - Return Copilot-Session-Token from copilot provider --- lua/CopilotChat/client.lua | 15 +++++++++++++-- lua/CopilotChat/config/providers.lua | 3 +-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lua/CopilotChat/client.lua b/lua/CopilotChat/client.lua index 7bbc65d8..9fea7d1c 100644 --- a/lua/CopilotChat/client.lua +++ b/lua/CopilotChat/client.lua @@ -319,13 +319,20 @@ function Client:ask(opts) error('Provider not found: ' .. provider_name) end + local requested_model = opts.model + 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 - error('Resolved model not found: ' .. opts.model) + if requested_model == 'auto' then + model_config = vim.tbl_extend('force', models[requested_model], { id = opts.model }) + else + error('Resolved model not found: ' .. opts.model) + end end end @@ -538,6 +545,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/config/providers.lua b/lua/CopilotChat/config/providers.lua index ea5eea71..1c6371ce 100644 --- a/lua/CopilotChat/config/providers.lua +++ b/lua/CopilotChat/config/providers.lua @@ -655,7 +655,6 @@ M.copilot = { tools = model.capabilities.supports.tool_calls, policy = not model['policy'] or model['policy']['state'] == 'enabled', version = model.version, - multiplier = model.billing and model.billing.multiplier or nil, use_responses = use_responses, -- Carry the base URL into the model so get_url and resolve_model -- can use it without needing access to the headers again. @@ -714,7 +713,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) From 5c3db7019a7f95a5f17a00a8c8d5c077a025c08b Mon Sep 17 00:00:00 2001 From: Michael Rosenfeld Date: Thu, 25 Jun 2026 16:57:55 -0400 Subject: [PATCH 2/3] chore: run pre-commit hooks --- .github/workflows/pullfrog.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 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 From f802c9b183d94aebb0c3044c01b73a8ff2b2c679 Mon Sep 17 00:00:00 2001 From: Michael Rosenfeld Date: Thu, 25 Jun 2026 17:13:29 -0400 Subject: [PATCH 3/3] feat(config): support model resolver lookup and billing multiplier Update provider config to allow resolve_model to return model id and lookup table. Add model multiplier mapping for Copilot provider and align README type definitions. --- README.md | 2 +- lua/CopilotChat/config/providers.lua | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 434cae00..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, + resolve_model?(headers: table, model: string): string, table?, } ``` diff --git a/lua/CopilotChat/config/providers.lua b/lua/CopilotChat/config/providers.lua index 1c6371ce..ad521d1f 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 @@ -655,6 +655,7 @@ M.copilot = { tools = model.capabilities.supports.tool_calls, policy = not model['policy'] or model['policy']['state'] == 'enabled', version = model.version, + multiplier = model.billing and model.billing.multiplier or nil, use_responses = use_responses, -- Carry the base URL into the model so get_url and resolve_model -- can use it without needing access to the headers again.