From 70e8c1ba0bc338d02d86cfef9c82ca549b387685 Mon Sep 17 00:00:00 2001 From: Jordi Been Date: Mon, 11 Aug 2025 10:07:25 +0200 Subject: [PATCH 1/5] add thinking capabilities for Anthropic models --- lua/gp/config.lua | 10 ++++++++++ lua/gp/dispatcher.lua | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/lua/gp/config.lua b/lua/gp/config.lua index 43cf729..cbfb196 100644 --- a/lua/gp/config.lua +++ b/lua/gp/config.lua @@ -171,6 +171,16 @@ local config = { -- system prompt (use this to specify the persona/role of the AI) system_prompt = require("gp.defaults").chat_system_prompt, }, + { + provider = "anthropic", + name = "ChatClaude-Sonnet-4-Thinking", + chat = true, + command = false, + -- string with model name or table with model name and parameters + model = { model = "claude-sonnet-4-20250514", thinking_budget = 1024 }, + -- system prompt (use this to specify the persona/role of the AI) + system_prompt = require("gp.defaults").chat_system_prompt, + }, { provider = "anthropic", name = "ChatClaude-3-5-Haiku", diff --git a/lua/gp/dispatcher.lua b/lua/gp/dispatcher.lua index c420977..c9e0bbc 100644 --- a/lua/gp/dispatcher.lua +++ b/lua/gp/dispatcher.lua @@ -158,6 +158,14 @@ D.prepare_payload = function(messages, model, provider) temperature = model.temperature and math.max(0, math.min(2, model.temperature)) or nil, top_p = model.top_p and math.max(0, math.min(1, model.top_p)) or nil, } + + if model.thinking_budget ~= nil then + payload.thinking = { + type = "enabled", + budget_tokens = model.thinking_budget + } + end + return payload end @@ -226,6 +234,7 @@ local query = function(buf, provider, payload, handler, on_exit, callback) last_line = -1, ns_id = nil, ex_id = nil, + in_thinking_block = false, }) local out_reader = function() @@ -252,14 +261,33 @@ local query = function(buf, provider, payload, handler, on_exit, callback) end end - if qt.provider == "anthropic" and line:match('"text":') then + if qt.provider == "anthropic" and (line:match('"text":') or line:match('"thinking"')) then if line:match("content_block_start") or line:match("content_block_delta") then line = vim.json.decode(line) - if line.delta and line.delta.text then - content = line.delta.text + + -- Handle content block start events + if line.content_block then + if line.content_block.type == "thinking" then + qt.in_thinking_block = true + content = "" + elseif line.content_block.type == "text" then + -- If we were in a thinking block, close it + if qt.in_thinking_block then + qt.in_thinking_block = false + content = "\n\n" + end + end end - if line.content_block and line.content_block.text then - content = line.content_block.text + + -- Handle content block delta events + if line.delta then + if line.delta.type == "thinking_delta" then + content = line.delta.thinking or "" + elseif line.delta.type == "text_delta" then + content = line.delta.text or "" + elseif line.delta.text then + content = line.delta.text + end end end end From a1fc9bdd60bcb738111f38625b17bf06494f9225 Mon Sep 17 00:00:00 2001 From: Jordi Been Date: Mon, 11 Aug 2025 10:07:38 +0200 Subject: [PATCH 2/5] remove redundant header --- lua/gp/dispatcher.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/lua/gp/dispatcher.lua b/lua/gp/dispatcher.lua index c9e0bbc..62f45de 100644 --- a/lua/gp/dispatcher.lua +++ b/lua/gp/dispatcher.lua @@ -410,8 +410,6 @@ local query = function(buf, provider, payload, handler, on_exit, callback) "x-api-key: " .. bearer, "-H", "anthropic-version: 2023-06-01", - "-H", - "anthropic-beta: messages-2023-12-15", } elseif provider == "azure" then headers = { From b9bbb19f38a2ed41a81e3077913ed4da16909e3d Mon Sep 17 00:00:00 2001 From: Jordi Been Date: Mon, 11 Aug 2025 10:17:27 +0200 Subject: [PATCH 3/5] clean up thinking determination logic --- lua/gp/dispatcher.lua | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/lua/gp/dispatcher.lua b/lua/gp/dispatcher.lua index 62f45de..a7770ed 100644 --- a/lua/gp/dispatcher.lua +++ b/lua/gp/dispatcher.lua @@ -264,29 +264,20 @@ local query = function(buf, provider, payload, handler, on_exit, callback) if qt.provider == "anthropic" and (line:match('"text":') or line:match('"thinking"')) then if line:match("content_block_start") or line:match("content_block_delta") then line = vim.json.decode(line) - - -- Handle content block start events if line.content_block then if line.content_block.type == "thinking" then qt.in_thinking_block = true content = "" - elseif line.content_block.type == "text" then - -- If we were in a thinking block, close it - if qt.in_thinking_block then - qt.in_thinking_block = false - content = "\n\n" - end + elseif line.content_block.type == "text" and qt.in_thinking_block then + qt.in_thinking_block = false + content = "\n\n" end end - - -- Handle content block delta events if line.delta then if line.delta.type == "thinking_delta" then content = line.delta.thinking or "" elseif line.delta.type == "text_delta" then - content = line.delta.text or "" - elseif line.delta.text then - content = line.delta.text + content = line.delta.text or "" end end end From 03a7aeb0e2b56a907bf6e8a024e1a72827375123 Mon Sep 17 00:00:00 2001 From: Jordi Been Date: Mon, 11 Aug 2025 10:20:45 +0200 Subject: [PATCH 4/5] cleaner way of keeping track of thinking state --- lua/gp/dispatcher.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/gp/dispatcher.lua b/lua/gp/dispatcher.lua index a7770ed..7e2ab46 100644 --- a/lua/gp/dispatcher.lua +++ b/lua/gp/dispatcher.lua @@ -234,11 +234,11 @@ local query = function(buf, provider, payload, handler, on_exit, callback) last_line = -1, ns_id = nil, ex_id = nil, - in_thinking_block = false, }) local out_reader = function() local buffer = "" + local anthropic_thinking = false -- local state for Anthropic thinking blocks ---@param lines_chunk string local function process_lines(lines_chunk) @@ -266,10 +266,10 @@ local query = function(buf, provider, payload, handler, on_exit, callback) line = vim.json.decode(line) if line.content_block then if line.content_block.type == "thinking" then - qt.in_thinking_block = true + anthropic_thinking = true content = "" - elseif line.content_block.type == "text" and qt.in_thinking_block then - qt.in_thinking_block = false + elseif line.content_block.type == "text" and anthropic_thinking then + anthropic_thinking = false content = "\n\n" end end From dbb942adb3b096598ac40fb31eaf6dfe95dddf23 Mon Sep 17 00:00:00 2001 From: Jordi Been Date: Mon, 11 Aug 2025 10:30:10 +0200 Subject: [PATCH 5/5] disable anthropic thinking for topic creation --- lua/gp/init.lua | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lua/gp/init.lua b/lua/gp/init.lua index d693981..c05ab7f 100644 --- a/lua/gp/init.lua +++ b/lua/gp/init.lua @@ -1159,11 +1159,17 @@ M.chat_respond = function(params) local topic_buf = vim.api.nvim_create_buf(false, true) local topic_handler = M.dispatcher.create_handler(topic_buf, nil, 0, false, "", false) - -- call the model + -- call the model (remove thinking_budget for Anthropic topic generation) + local topic_model = headers.model or agent.model + local provider = headers.provider or agent.provider + if provider == "anthropic" and topic_model.thinking_budget then + topic_model = vim.deepcopy(topic_model) + topic_model.thinking_budget = nil + end M.dispatcher.query( nil, - headers.provider or agent.provider, - M.dispatcher.prepare_payload(messages, headers.model or agent.model, headers.provider or agent.provider), + provider, + M.dispatcher.prepare_payload(messages, topic_model, provider), topic_handler, vim.schedule_wrap(function() -- get topic from invisible buffer