Skip to content

Commit

Permalink
temp: tools.lua
Browse files Browse the repository at this point in the history
  • Loading branch information
default-anton committed Feb 1, 2025
1 parent ba8d4b6 commit 085f2e9
Show file tree
Hide file tree
Showing 10 changed files with 827 additions and 7 deletions.
5 changes: 4 additions & 1 deletion lua/llm-sidekick/anthropic.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ function anthropic.new(url)
)
end

function anthropic:chat(messages, settings, callback)
function anthropic:chat(opts, callback)
local messages = opts.messages
local settings = opts.settings
local tools = opts.tools
callback = vim.schedule_wrap(callback)

local data = {
Expand Down
5 changes: 4 additions & 1 deletion lua/llm-sidekick/bedrock.lua
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,10 @@ function bedrock.start_web_server()
}):start()
end

function bedrock:chat(messages, settings, callback)
function bedrock:chat(opts, callback)
local messages = opts.messages
local settings = opts.settings
local tools = opts.tools
callback = vim.schedule_wrap(callback)

local data = {
Expand Down
176 changes: 176 additions & 0 deletions lua/llm-sidekick/bedrock_tool_use.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
local message_types = require("llm-sidekick.message_types")
local Job = require("plenary.job")
local bedrock = {}

function bedrock.new(url)
return setmetatable({ url = url or 'http://localhost:5500/invoke' },
{ __index = bedrock }
)
end

local function command_exists(cmd)
local exists = false
Job:new({
command = 'which',
args = { cmd },
on_exit = function(_, return_val)
exists = (return_val == 0)
end,
}):sync()
return exists
end

function bedrock.start_web_server()
if not command_exists('uv') then
vim.schedule(function()
vim.api.nvim_err_writeln("Error: 'uv' command is not available")
end)
return
end

local plugin_root = vim.fn.fnamemodify(vim.fn.resolve(debug.getinfo(1, "S").source:sub(2)), ":h:h:h")

Job:new({
command = 'uv',
detached = true,
args = {
'run',
'--python', '3.12',
'--with', 'gunicorn',
'--with', 'boto3',
'--with', 'flask-cors',
'gunicorn',
'--bind', '0.0.0.0:5500',
'--timeout', '300',
'--keep-alive', '300',
'--access-logfile', '/tmp/llm-sidekick-access.log',
'--error-logfile', '/tmp/llm-sidekick-error.log',
'--pid', '/tmp/llm-sidekick.pid',
'--pythonpath', plugin_root,
'python.bedrock:app'
},
on_error = function(_, error)
vim.schedule(function()
vim.api.nvim_err_writeln("Error starting web server: " .. tostring(error))
end)
end,
on_stderr = function(_, data)
if data and data ~= "" and not string.find(data, "Already running on PID", 1, true) then
vim.schedule(function()
vim.api.nvim_err_writeln("Web server stderr: " .. data)
end)
end
end,
}):start()
end

function bedrock:chat(opts)
local messages = opts.messages
local tools = opts.tools
local settings = opts.settings
local callback = vim.schedule_wrap(opts.callback)

local data = {
model_id = settings.model,
body = {
temperature = settings.temperature,
max_tokens = settings.max_tokens,
messages = messages,
tools = tools,
},
}

local us_west_2_models = { "anthropic.claude-3-5-sonnet-20241022-v2:0", "anthropic.claude-3-5-haiku-20241022-v1:0" }
if vim.tbl_contains(us_west_2_models, settings.model) then
data["region"] = "us-west-2"
end

for i, message in ipairs(data.body.messages) do
if message.role == "system" then
data.body.system = message.content
table.remove(data.body.messages, i)
break
end
end

local curl = require("llm-sidekick.executables").get_curl_executable()
local json_data = vim.json.encode(data)
local args = {
'-s',
'--no-buffer',
'-H', 'Content-Type: application/json',
'-H', 'anthropic-version: bedrock-2023-05-31',
'-d', json_data,
self.url,
}

local tool = nil

Job:new({
command = curl,
args = args,
on_stdout = function(_, line)
line = line:gsub("^data: ", "")
if line == "" then
return
end
local ok, decoded = pcall(vim.json.decode, line)
if not ok or not decoded then
vim.schedule(function()
vim.api.nvim_err_writeln("Error: Failed to decode JSON line: " .. line)
end)
return
end

if decoded.type == "content_block_start" then
if decoded.content_block and decoded.content_block.type == "tool_use" then
tool = {
id = decoded.content_block.id,
name = decoded.content_block.name,
partial_json = ""
}
else
tool = nil
end
elseif decoded.type == "content_block_stop" then
tool = nil
elseif decoded.type == "content_block_delta" then
if decoded.delta.type == "text_delta" and decoded.delta.text then
callback(message_types.DATA, decoded.delta.text)
elseif decoded.delta.type == "input_json_delta" and decoded.delta.partial_json then
tool.partial_json = tool.partial_json .. decoded.delta.partial_json
callback(message_types.DATA, decoded.delta.partial_json)
end
elseif decoded.type == "message_delta" then
if decoded.delta and decoded.delta.stop_reason then
callback(message_types.DONE, "")
end
elseif decoded.error then
callback(message_types.DONE, "")
vim.schedule(function()
vim.api.nvim_err_writeln("Error: " .. decoded.error)
end)
end
end,
on_stderr = function(_, text)
if not text or text == "" then
return
end

vim.schedule(function()
vim.api.nvim_err_writeln("Error: " .. tostring(text))
end)
end,
on_exit = function(_, return_val)
if return_val == 0 then
return
end

vim.schedule(function()
vim.api.nvim_err_writeln("Error: API request failed with exit code " .. return_val)
end)
end,
}):start()
end

return bedrock
5 changes: 4 additions & 1 deletion lua/llm-sidekick/gemini.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ function gemini.new(url)
)
end

function gemini:chat(messages, settings, callback)
function gemini:chat(opts, callback)
local messages = opts.messages
local settings = opts.settings
local tools = opts.tools
callback = vim.schedule_wrap(callback)

local system_message = nil
Expand Down
5 changes: 3 additions & 2 deletions lua/llm-sidekick/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ function M.parse_prompt(prompt)
media_type = mime_type,
},
}
elseif vim.startswith(model_name, "gpt") or model_name == "o1" or vim.startswith(model_name, "o3") then
elseif vim.startswith(model_name, "gpt") or model_name == "o1" then
image = {
type = "image_url",
image_url = { url = string.format("data:%s;base64,%s", mime_type, base64_image) },
Expand Down Expand Up @@ -151,6 +151,7 @@ function M.ask(prompt_bufnr)
local buf_lines = vim.api.nvim_buf_get_lines(prompt_bufnr, 0, -1, false)
local full_prompt = table.concat(buf_lines, "\n")
local prompt = M.parse_prompt(full_prompt)
prompt.tools = require("llm-sidekick.tools")

local model_settings = settings.get_model_settings(prompt.settings.model)
prompt.settings.model = model_settings.name
Expand Down Expand Up @@ -204,7 +205,7 @@ function M.ask(prompt_bufnr)

local in_reasoning_tag = false

client:chat(prompt.messages, prompt.settings, function(state, chars)
client:chat(prompt, function(state, chars)
if not vim.api.nvim_buf_is_valid(prompt_bufnr) then
return
end
Expand Down
2 changes: 2 additions & 0 deletions lua/llm-sidekick/message_types.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
return {
DATA = "data",
REASONING = "reasoning",
PARTIAL_FUNCTION_CALL = "partial_function_call",
FUNCTION_CALL = "function_call",
DONE = "done",
}
22 changes: 20 additions & 2 deletions lua/llm-sidekick/openai.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ function openai.new(opts)
)
end

function openai:chat(messages, settings, callback)
function openai:chat(opts, callback)
local messages = opts.messages
local settings = opts.settings
local tools = opts.tools
callback = vim.schedule_wrap(callback)
local data = {
model = settings.model,
Expand Down Expand Up @@ -87,9 +90,24 @@ function openai:chat(messages, settings, callback)
end)
end,
on_exit = function(j, return_val)
if j:result() and not vim.tbl_isempty(j:result()) then
vim.schedule(function()
local ok, res = pcall(vim.json.decode, table.concat(j:result(), "\n"))
if ok and res and res.error then
vim.notify("Error: " .. res.error.message, vim.log.levels.ERROR)
end
end)
end

if return_val ~= 0 then
vim.schedule(function()
vim.api.nvim_err_writeln("Error: API request failed with exit code " .. return_val)
if j:result() and not vim.tbl_isempty(j:result()) then
vim.notify("Error: " .. table.concat(j:result(), "\n"), vim.log.levels.ERROR)
end

if j:stderr_result() and not vim.tbl_isempty(j:stderr_result()) then
vim.notify("Error: " .. table.concat(j:stderr_result(), "\n"), vim.log.levels.ERROR)
end
end)
return
end
Expand Down
Loading

0 comments on commit 085f2e9

Please sign in to comment.