Skip to content

Commit 828ddad

Browse files
committed
wip
1 parent f6dd6d6 commit 828ddad

7 files changed

Lines changed: 140 additions & 50 deletions

File tree

lua/codecompanion/adapters/http/anthropic.lua

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local adapter_utils = require("codecompanion.utils.adapters")
22
local log = require("codecompanion.utils.log")
33
local transform = require("codecompanion.utils.tool_transformers")
44

5+
56
---@class CodeCompanion.HTTPAdapter.Anthropic: CodeCompanion.HTTPAdapter
67
return {
78
name = "anthropic",
@@ -15,7 +16,7 @@ return {
1516
text = true,
1617
},
1718
opts = {
18-
context_management = true,
19+
-- context_management = true,
1920
stream = true,
2021
tools = true,
2122
vision = true,
@@ -39,8 +40,7 @@ return {
3940
---@param self CodeCompanion.HTTPAdapter.Anthropic
4041
---@param meta { tools: table }
4142
callback = function(self, meta)
42-
local beta = self.headers["anthropic-beta"]
43-
self.headers["anthropic-beta"] = (beta and (beta .. ",") or "") .. "code-execution-2025-08-25"
43+
adapter_utils.add_header(self.headers, "anthropic-beta", "code-execution-2025-08-25")
4444

4545
table.insert(meta.tools, {
4646
type = "code_execution_20250825",
@@ -53,8 +53,7 @@ return {
5353
---@param self CodeCompanion.HTTPAdapter.Anthropic
5454
---@param meta { tools: table }
5555
callback = function(self, meta)
56-
local beta = self.headers["anthropic-beta"]
57-
self.headers["anthropic-beta"] = (beta and (beta .. ",") or "") .. "context-management-2025-06-27"
56+
adapter_utils.add_header(self.headers, "anthropic-beta", "context-management-2025-06-27")
5857

5958
table.insert(meta.tools, {
6059
type = "memory_20250818",
@@ -71,8 +70,7 @@ return {
7170
---@param self CodeCompanion.HTTPAdapter.Anthropic
7271
---@param meta { tools: table }
7372
callback = function(self, meta)
74-
local beta = self.headers["anthropic-beta"]
75-
self.headers["anthropic-beta"] = (beta and (beta .. ",") or "") .. "web-fetch-2025-09-10"
73+
adapter_utils.add_header(self.headers, "anthropic-beta", "web-fetch-2025-09-10")
7674

7775
table.insert(meta.tools, {
7876
type = "web_fetch_20250910",
@@ -114,21 +112,18 @@ return {
114112

115113
-- Add the extended output header if enabled
116114
if self.temp.extended_output then
117-
local beta = self.headers["anthropic-beta"]
118-
self.headers["anthropic-beta"] = (beta and (beta .. ",") or "") .. "output-128k-2025-02-19"
115+
adapter_utils.add_header(self.headers, "anthropic-beta", "output-128k-2025-02-19")
119116
end
120117

121118
-- Ref: https://docs.anthropic.com/en/docs/build-with-claude/tool-use/token-efficient-tool-use
122119
if self.opts.has_token_efficient_tools then
123-
local beta = self.headers["anthropic-beta"]
124-
self.headers["anthropic-beta"] = (beta and (beta .. ",") or "") .. "token-efficient-tools-2025-02-19"
120+
adapter_utils.add_header(self.headers, "anthropic-beta", "token-efficient-tools-2025-02-19")
125121
end
126122

127123
-- Ref: https://platform.claude.com/docs/en/build-with-claude/context-editing#tool-result-clearing-usage
128-
if self.opts.context_management then
129-
local beta = self.headers["anthropic-beta"]
130-
self.headers["anthropic-beta"] = (beta and (beta .. ",") or "") .. "context-management-2025-06-27"
131-
end
124+
-- if self.opts.context_management then
125+
-- adapter_utils.add_header(self.headers, "anthropic-beta", "context-management-2025-06-27")
126+
-- end
132127

133128
return true
134129
end,
@@ -322,39 +317,46 @@ return {
322317
end
323318
end
324319

325-
local context_management = nil
326-
if self.opts.context_management then
327-
context_management = {
328-
["edits"] = {
329-
{
330-
type = "clear_thinking_20251015",
331-
keep = {
332-
type = "thinking_turns",
333-
value = 3,
334-
},
335-
},
336-
{
337-
type = "clear_tool_uses_20250919",
338-
keep = {
339-
type = "tool_uses",
340-
value = 5,
341-
},
342-
trigger = {
343-
type = "input_tokens",
344-
value = 50000,
345-
},
346-
},
347-
},
348-
}
349-
end
320+
-- local context_management = nil
321+
-- if self.opts.context_management then
322+
-- context_management = {
323+
-- ["edits"] = {
324+
-- {
325+
-- type = "clear_thinking_20251015",
326+
-- keep = {
327+
-- type = "thinking_turns",
328+
-- value = 3,
329+
-- },
330+
-- },
331+
-- {
332+
-- type = "clear_tool_uses_20250919",
333+
-- keep = {
334+
-- type = "tool_uses",
335+
-- value = 5,
336+
-- },
337+
-- trigger = {
338+
-- type = "input_tokens",
339+
-- value = 50000,
340+
-- },
341+
-- },
342+
-- -- {
343+
-- -- type = "compact_20260112",
344+
-- -- trigger = {
345+
-- -- type = "input_tokens",
346+
-- -- value = 100000,
347+
-- -- },
348+
-- -- },
349+
-- },
350+
-- }
351+
-- end
350352

351353
-- 11. Enable automatic prompt caching
352354
-- Ref: https://platform.claude.com/docs/en/build-with-claude/prompt-caching#automatic-caching
353355
return {
354356
cache_control = { type = "ephemeral" },
355357
system = system,
356358
messages = messages,
357-
context_management = context_management,
359+
-- context_management = context_management,
358360
}
359361
end,
360362

lua/codecompanion/adapters/http/init.lua

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ end
111111

112112
---@class CodeCompanion.HTTPAdapter
113113
---@field name string The name of the adapter
114+
---@field vendor? string The vendor of the adapter, e.g. "openai" or "azure"
114115
---@field type string|"http" The type of the adapter, e.g. "http" or "acp"
115116
---@field formatted_name string The formatted name of the adapter
116117
---@field available_tools? table The tools that are available for the adapter
@@ -128,22 +129,25 @@ end
128129
---@field handlers CodeCompanion.HTTPAdapter.Handlers Functions which link the output from the request to CodeCompanion
129130
---@field meta? { context_window: number } Data about the selected model
130131
---@field methods table Methods that the adapter can perform e.g. for Slash Commands
131-
---@field model? { name: string, formatted_name?: string, vendor?: string, opts: table, info?: table } The model to use for the request
132+
---@field model? { name: string, formatted_name?: string, info?: table, meta?: table, opts: table, vendor?: string } The model to use for the request
132133
---@field opts? table Additional options for the adapter
133134
---@field schema table Set of parameters for the generative AI service that the user can customise in the chat buffer
134135
---@field temp? table A table to store temporary values which are not passed to the request
135136

136137
---@class CodeCompanion.HTTPAdapter.Safe
137138
---@field name string The name of the adapter
138-
---@field model string The current model name
139+
---@field vendor? string The vendor of the adapter, e.g. "openai" or "azure"
140+
---@field type string|"http" The type of the adapter, e.g. "http" or "acp"
141+
---@field model table The current model name
139142
---@field available_tools? table The tools that are available for the adapter
140143
---@field formatted_name string The formatted name of the adapter
141144
---@field features table The features that the adapter supports
142145
---@field url string The URL of the generative AI service to connect to
143146
---@field headers table The headers to pass to the request
144147
---@field parameters table The parameters to pass to the request
145-
---@field opts? table Additional options for the adapter
146148
---@field handlers CodeCompanion.HTTPAdapter.Handlers Functions which link the output from the request to CodeCompanion
149+
---@field opts? table Additional options for the adapter
150+
---@field meta? table Data about the selected model
147151
---@field schema table Set of parameters for the generative AI service that the user can customise in the chat buffer
148152

149153
---@class CodeCompanion.HTTPAdapter
@@ -281,11 +285,16 @@ function Adapter.set_model(args)
281285
local model = adapter.schema.model.default
282286
local choices = adapter.schema.model.choices
283287

288+
adapter.model.vendor = adapter.vendor or adapter.name
289+
284290
if type(model) == "string" then
285291
adapter.model.name = model
286292
end
287293
if type(choices) == "table" then
288294
adapter.model.opts = (choices[model] and choices[model].opts) and choices[model].opts
295+
adapter.model.meta = (choices[model] and choices[model].meta) and choices[model].meta
296+
adapter.model.formatted_name = (choices[model] and choices[model].formatted_name)
297+
and choices[model].formatted_name
289298
end
290299
end
291300

@@ -359,6 +368,7 @@ end
359368
function Adapter.make_safe(adapter)
360369
return {
361370
name = adapter.name,
371+
vendor = adapter.vendor,
362372
type = adapter.type,
363373
model = adapter.model,
364374
available_tools = adapter.available_tools,
@@ -367,8 +377,9 @@ function Adapter.make_safe(adapter)
367377
url = adapter.url,
368378
headers = adapter.headers,
369379
parameters = adapter.parameters,
370-
opts = adapter.opts,
371380
handlers = adapter.handlers,
381+
opts = adapter.opts,
382+
meta = adapter.meta,
372383
schema = vim
373384
.iter(adapter.schema)
374385
:filter(function(n, _)

lua/codecompanion/adapters/http/openai_responses.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ end
2424
---@class CodeCompanion.HTTPAdapter.OpenAIResponses: CodeCompanion.HTTPAdapter
2525
return {
2626
name = "openai_responses",
27+
vendor = "openai",
2728
formatted_name = "OpenAI_Responses",
2829
roles = {
2930
llm = "assistant",
@@ -545,7 +546,7 @@ return {
545546
type = "enum",
546547
desc = "ID of the model to use. See the model endpoint compatibility table for details on which models work with the Chat API.",
547548
---@type string|fun(): string
548-
default = "gpt-5-2025-08-07",
549+
default = "gpt-5",
549550
choices = {
550551
["gpt-5.4-pro"] = {
551552
formatted_name = "GPT 5.4 Pro",

lua/codecompanion/config.lua

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ local defaults = {
8282
},
8383
enabled = true,
8484
},
85+
["at_token_limit"] = {
86+
actions = {
87+
"interactions.background.builtin.chat_make_title",
88+
},
89+
enabled = true,
90+
},
8591
},
8692
opts = {
8793
enabled = false, -- Enable ALL background chat interactions?
@@ -664,6 +670,20 @@ If you are providing code changes, use the insert_edit_into_file tool (if availa
664670
},
665671
},
666672
opts = {
673+
compaction = {
674+
trigger = 0.75, -- Compaction starts at 75% of the context window limit
675+
enabled = function(adapter)
676+
if adapter.type ~= "http" then
677+
return false
678+
end
679+
-- Anthropic and OpenAI have their own server-side compaction
680+
if adapter.vendor and (adapter.vendor == "anthropic" or adapter.vendor == "openai") then
681+
return false
682+
end
683+
return true
684+
end,
685+
},
686+
667687
blank_prompt = "", -- The prompt to use when the user doesn't provide a prompt
668688
completion_provider = providers.completion, -- blink|cmp|coc|default
669689
debounce = 150, -- Time to debounce user input (milliseconds)
@@ -672,9 +692,6 @@ If you are providing code changes, use the insert_edit_into_file tool (if availa
672692
wait_timeout = 2e6, -- Time to wait for user response before timing out (milliseconds)
673693
yank_jump_delay_ms = 400, -- Delay before jumping back from the yanked code (milliseconds )
674694

675-
-- What to do when an ACP permission request times out? (allow_once|reject_once)
676-
acp_timeout_response = "reject_once",
677-
678695
---@type string|fun(path: string)
679696
goto_file_action = ui_utils.tabnew_reuse,
680697

lua/codecompanion/interactions/chat/init.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,9 @@ function Chat.new(args)
610610
self:add_callback("on_ready", function(c)
611611
c.subscribers:process(c)
612612
end)
613+
self:add_callback("at_token_limit", function(c)
614+
c.subscribers:process(c)
615+
end)
613616
self:add_callback("on_cancelled", function(c)
614617
c.subscribers:stop()
615618
end)

lua/codecompanion/utils/adapters.lua

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,26 @@ function M.set_env_vars(adapter, object)
357357
end
358358
end
359359

360+
---Add a value to a comma-separated header without duplicating existing values
361+
---@param headers table The headers table to modify
362+
---@param key string The header name
363+
---@param value string The value to add
364+
function M.add_header(headers, key, value)
365+
local existing = headers[key]
366+
if not existing then
367+
headers[key] = value
368+
return
369+
end
370+
371+
for entry in existing:gmatch("[^,]+") do
372+
if vim.trim(entry) == value then
373+
return
374+
end
375+
end
376+
377+
headers[key] = existing .. "," .. value
378+
end
379+
360380
---Replace roles in the messages with the adapter's defined roles
361381
---@param roles table The roles mapping, e.g. { user = "human", assistant = "ai" }
362382
---@param messages table

tests/adapters/test_adapters.lua

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ T["HTTP Adapter"]["can update a model on the adapter"] = function()
271271
return adapter.resolve(adapter).model
272272
]])
273273

274-
h.eq({ name = "gpt-4-0125-preview" }, result)
274+
h.eq({ name = "gpt-4-0125-preview", vendor = "TestAdapter" }, result)
275275

276276
result = child.lua([[
277277
local adapter = require("codecompanion.adapters").extend("openai", {
@@ -291,11 +291,13 @@ T["HTTP Adapter"]["can update a model on the adapter"] = function()
291291
]])
292292

293293
h.eq({
294+
formatted_name = "o4 Mini",
294295
name = "o4-mini-2025-04-16",
295296
opts = {
296297
can_reason = true,
297298
has_vision = true,
298299
},
300+
vendor = "openai",
299301
}, result)
300302
end
301303

@@ -578,6 +580,40 @@ T["Adapter"]["utils"]["can smartly merge tables together"] = function()
578580
}, child.lua_get([[utils.merge_messages(messages)]]))
579581
end
580582

583+
T["Adapter"]["utils"]["add_header sets header when absent"] = function()
584+
local result = child.lua([[
585+
local headers = {}
586+
utils.add_header(headers, "x-custom", "value-a")
587+
return headers
588+
]])
589+
590+
h.eq({ ["x-custom"] = "value-a" }, result)
591+
end
592+
593+
T["Adapter"]["utils"]["add_header appends without duplicating"] = function()
594+
local result = child.lua([[
595+
local headers = { ["anthropic-beta"] = "token-efficient-tools-2025-02-19" }
596+
utils.add_header(headers, "anthropic-beta", "output-128k-2025-02-19")
597+
utils.add_header(headers, "anthropic-beta", "token-efficient-tools-2025-02-19")
598+
return headers["anthropic-beta"]
599+
]])
600+
601+
h.eq("token-efficient-tools-2025-02-19,output-128k-2025-02-19", result)
602+
end
603+
604+
T["Adapter"]["utils"]["add_header handles multiple values"] = function()
605+
local result = child.lua([[
606+
local headers = {}
607+
utils.add_header(headers, "anthropic-beta", "a")
608+
utils.add_header(headers, "anthropic-beta", "b")
609+
utils.add_header(headers, "anthropic-beta", "c")
610+
utils.add_header(headers, "anthropic-beta", "b")
611+
return headers["anthropic-beta"]
612+
]])
613+
614+
h.eq("a,b,c", result)
615+
end
616+
581617
T["Adapter"]["utils"]["can consolidate system messages"] = function()
582618
child.lua([[
583619
messages = {

0 commit comments

Comments
 (0)