local Helpers = require("avante.history.helpers") local Line = require("avante.ui.line") local Utils = require("avante.utils") local M = {} ---Converts text into format suitable for UI ---@param text string ---@return avante.ui.Line[] local function text_to_lines(text) local text_lines = vim.split(text, "\n") local lines = {} for _, text_line in ipairs(text_lines) do table.insert(lines, Line:new({ { text_line } })) end return lines end ---Converts "thinking" item into format suitable for UI ---@param item AvanteLLMMessageContentItem ---@return avante.ui.Line[] local function thinking_to_lines(item) local text = item.thinking or item.data or "" local text_lines = vim.split(text, "\n") local ui_lines = {} table.insert(ui_lines, Line:new({ { Utils.icon("🤔 ") .. "Thought content:" } })) table.insert(ui_lines, Line:new({ { "" } })) for _, text_line in ipairs(text_lines) do table.insert(ui_lines, Line:new({ { "> " .. text_line } })) end return ui_lines end ---Converts logs generated by a tool during execution into format suitable for UI ---@param tool_name string ---@param logs string[] ---@return avante.ui.Line[] local function tool_logs_to_lines(tool_name, logs) local ui_lines = {} local num_logs = #logs for log_idx = 1, num_logs do local log_lines = vim.split(logs[log_idx]:gsub("^%[" .. tool_name .. "%]: ", "", 1), "\n") local num_lines = #log_lines for line_idx = 1, num_lines do local decoration = (log_idx == num_logs and line_idx == num_lines) and "╰─ " or "│ " table.insert(ui_lines, Line:new({ { decoration }, { " " .. log_lines[line_idx] } })) end end return ui_lines end local STATE_TO_HL = { generating = "AvanteStateSpinnerToolCalling", failed = "AvanteStateSpinnerFailed", succeeded = "AvanteStateSpinnerSucceeded", } ---Converts a tool invocation into format suitable for UI ---@param item AvanteLLMMessageContentItem ---@param messages avante.HistoryMessage[] ---@param logs string[]|nil ---@return avante.ui.Line[] local function tool_to_lines(item, messages, logs) local lines = {} local result = Helpers.get_tool_result(item.id, messages) local state if not result then state = "generating" elseif result.is_error then state = "failed" else state = "succeeded" end table.insert( lines, Line:new({ { "╭─ " }, { " " .. item.name .. " ", STATE_TO_HL[state] }, { " " .. state }, }) ) if logs then vim.list_extend(lines, tool_logs_to_lines(item.name, logs)) end return lines end ---Converts a message item into representation suitable for UI ---@param item AvanteLLMMessageContentItem ---@param message avante.HistoryMessage ---@param messages avante.HistoryMessage[] ---@return avante.ui.Line[] local function message_content_item_to_lines(item, message, messages) if type(item) == "string" then return text_to_lines(item) elseif type(item) == "table" then if item.type == "thinking" or item.type == "redacted_thinking" then return thinking_to_lines(item.thinking or item.data or "") elseif item.type == "text" then return text_to_lines(item.text) elseif item.type == "image" then return { Line:new({ { "![image](" .. item.source.media_type .. ": " .. item.source.data .. ")" } }) } elseif item.type == "tool_use" then local ok, llm_tool = pcall(require, "avante.llm_tools." .. item.name) if ok then local tool_result_message = Helpers.get_tool_result_message(message, messages) ---@cast llm_tool AvanteLLMTool if llm_tool.on_render then return llm_tool.on_render(item.input, { logs = message.tool_use_logs, state = message.state, store = message.tool_use_store, result_message = tool_result_message, }) end end return tool_to_lines(item, messages, message.tool_use_logs) end end return {} end ---Converts a message into representation suitable for UI ---@param message avante.HistoryMessage ---@param messages avante.HistoryMessage[] ---@return avante.ui.Line[] function M.message_to_lines(message, messages) if message.displayed_content then return text_to_lines(message.displayed_content) end local content = message.message.content if type(content) == "string" then return text_to_lines(content) end if vim.islist(content) then local lines = {} for _, item in ipairs(content) do local item_lines = message_content_item_to_lines(item, message, messages) lines = vim.list_extend(lines, item_lines) end return lines end return {} end ---Converts a message item into text representation ---@param item AvanteLLMMessageContentItem ---@param message avante.HistoryMessage ---@param messages avante.HistoryMessage[] ---@return string local function message_content_item_to_text(item, message, messages) local lines = message_content_item_to_lines(item, message, messages) return vim.iter(lines):map(function(line) return tostring(line) end):join("\n") end ---Converts a message into text representation ---@param message avante.HistoryMessage ---@param messages avante.HistoryMessage[] ---@return string function M.message_to_text(message, messages) local content = message.message.content if type(content) == "string" then return content end if vim.islist(content) then return vim .iter(content) :map(function(item) return message_content_item_to_text(item, message, messages) end) :filter(function(text) return text ~= "" end) :join("\n") end return "" end return M