Felipe97/llama-cpp-compiled
01.1k
1#pragma once2 3#include "common.h"4#include "console.h"5 6#include <array>7#include <algorithm>8#include <cctype>9#include <filesystem>10#include <string_view>11 12// TODO?: Make this reusable, enums, docs13static const std::array<std::string_view, 8> cmds = {14 "/audio ",15 "/clear",16 "/exit",17 "/glob ",18 "/image ",19 "/read ",20 "/regen",21 "/video ",22};23 24static std::vector<std::pair<std::string, size_t>> auto_completion_callback(std::string_view line, size_t cursor_byte_pos) {25 std::vector<std::pair<std::string, size_t>> matches;26 std::string cmd;27 28 if (line.length() > 1 && line.front() == '/' && !std::any_of(cmds.begin(), cmds.end(), [line](std::string_view prefix) {29 return string_starts_with(line, prefix);30 })) {31 auto it = cmds.begin();32 33 while ((it = std::find_if(it, cmds.end(), [line](std::string_view cmd_line) {34 return string_starts_with(cmd_line, line);35 })) != cmds.end()) {36 matches.emplace_back(*it, it->length());37 ++it;38 }39 } else {40 auto it = std::find_if(cmds.begin(), cmds.end(), [line](std::string_view prefix) {41 return prefix.back() == ' ' && string_starts_with(line, prefix);42 });43 44 if (it != cmds.end()) {45 cmd = *it;46 }47 }48 49 if (!cmd.empty() && cmd != "/glob " && line.length() >= cmd.length() && cursor_byte_pos >= cmd.length()) {50 const std::string path_prefix = std::string(line.substr(cmd.length(), cursor_byte_pos - cmd.length()));51 const std::string path_postfix = std::string(line.substr(cursor_byte_pos));52 auto cur_dir = std::filesystem::current_path();53 std::string cur_dir_str = cur_dir.string();54 std::string expanded_prefix = path_prefix;55 56#if !defined(_WIN32)57 if (string_starts_with(path_prefix, '~')) {58 const char * home = std::getenv("HOME");59 if (home && home[0]) {60 expanded_prefix = home + path_prefix.substr(1);61 }62 }63 if (string_starts_with(expanded_prefix, '/')) {64#else65 if (std::isalpha(static_cast<unsigned char>(expanded_prefix[0])) && expanded_prefix.find(':') == 1) {66#endif67 cur_dir = std::filesystem::path(expanded_prefix).parent_path();68 cur_dir_str.clear();69 } else if (!path_prefix.empty()) {70 cur_dir /= std::filesystem::path(path_prefix).parent_path();71 }72 73 std::error_code ec;74 for (const auto & entry : std::filesystem::directory_iterator(cur_dir, ec)) {75 if (ec) {76 break;77 }78 if (!entry.exists(ec)) {79 ec.clear();80 continue;81 }82 83 const std::string path_full = entry.path().string();84 std::string path_entry = !cur_dir_str.empty() && string_starts_with(path_full, cur_dir_str) ? path_full.substr(cur_dir_str.length() + 1) : path_full;85 86 if (entry.is_directory(ec)) {87 path_entry.push_back(std::filesystem::path::preferred_separator);88 }89 90 if (expanded_prefix.empty() || string_starts_with(path_entry, expanded_prefix)) {91 const std::string updated_line = cmd + path_entry;92 matches.emplace_back(updated_line + path_postfix, updated_line.length());93 }94 95 if (ec) {96 ec.clear();97 }98 }99 100 if (matches.empty()) {101 const std::string updated_line = cmd + path_prefix;102 matches.emplace_back(updated_line + path_postfix, updated_line.length());103 }104 105 // Add the longest common prefix106 if (!expanded_prefix.empty() && matches.size() > 1) {107 const std::string_view match0(matches[0].first);108 const std::string_view match1(matches[1].first);109 auto it = std::mismatch(match0.begin(), match0.end(), match1.begin(), match1.end());110 size_t len = it.first - match0.begin();111 112 for (size_t i = 2; i < matches.size(); ++i) {113 const std::string_view matchi(matches[i].first);114 auto cmp = std::mismatch(match0.begin(), match0.end(), matchi.begin(), matchi.end());115 len = std::min(len, static_cast<size_t>(cmp.first - match0.begin()));116 }117 118 const std::string updated_line = std::string(match0.substr(0, len));119 matches.emplace_back(updated_line + path_postfix, updated_line.length());120 }121 122 std::sort(matches.begin(), matches.end(), [](const auto & a, const auto & b) {123 return a.first.compare(0, a.second, b.first, 0, b.second) < 0;124 });125 }126 127 return matches;128}129 130// note: make this view implementation generic, so that we can move to TUI in the future if we want to131namespace ui {132 static void init(const common_params & params) {133 // TODO: avoid using atexit() here by making `console` a singleton134 console::init(params.simple_io, params.use_color);135 atexit([]() { console::cleanup(); });136 137 console::set_completion_callback(auto_completion_callback);138 }139 140 struct spinner {141 spinner(const std::string & message) {142 if (!message.empty()) {143 console::log("%s ", message.c_str());144 }145 console::spinner::start();146 }147 ~spinner() {148 console::spinner::stop();149 }150 };151 152 struct user_turn {153 user_turn() {154 console::set_display(DISPLAY_TYPE_USER_INPUT);155 }156 ~user_turn() {157 console::set_display(DISPLAY_TYPE_RESET);158 }159 void echo(const std::string & buffer) {160 if (buffer.size() > 500) {161 console::log("\n> %s ... (truncated)\n", buffer.substr(0, 500).c_str());162 } else {163 console::log("\n> %s\n", buffer.c_str());164 }165 }166 std::string read_input(bool multiline_input, const char * prompt = nullptr) {167 if (prompt) {168 console::log("%s", prompt);169 } else {170 console::log("\n> ");171 }172 std::string buffer;173 std::string line;174 bool another_line = true;175 do {176 another_line = console::readline(line, multiline_input);177 buffer += line;178 } while (another_line);179 return buffer;180 }181 };182 183 enum assistant_display_mode {184 ASSISTANT_DISPLAY_MODE_REASONING,185 ASSISTANT_DISPLAY_MODE_CONTENT,186 };187 struct assistant_turn {188 assistant_display_mode mode = ASSISTANT_DISPLAY_MODE_CONTENT;189 bool trailing_newline = true;190 bool is_inside_reasoning = false;191 assistant_turn() {192 console::set_display(DISPLAY_TYPE_RESET);193 }194 ~assistant_turn() {195 console::set_display(DISPLAY_TYPE_RESET);196 add_newline_if_needed();197 }198 void push(assistant_display_mode m, const std::string & buffer) {199 if (m != mode) {200 add_newline_if_needed();201 switch (m) {202 case ASSISTANT_DISPLAY_MODE_CONTENT:203 {204 if (is_inside_reasoning) {205 console::log("[End thinking]\n\n");206 is_inside_reasoning = false;207 }208 console::set_display(DISPLAY_TYPE_RESET);209 } break;210 case ASSISTANT_DISPLAY_MODE_REASONING:211 {212 console::set_display(DISPLAY_TYPE_REASONING);213 is_inside_reasoning = true;214 console::log("\n[Start thinking]\n\n");215 } break;216 }217 }218 mode = m;219 if (buffer.empty()) {220 return;221 }222 trailing_newline = buffer.back() == '\n';223 console::log("%s", buffer.c_str());224 console::flush();225 }226 void add_newline_if_needed() {227 if (!trailing_newline) {228 console::log("\n");229 console::flush();230 }231 }232 };233 234 static void show_error(const std::string & title, const std::string & message = "") {235 console::spinner::stop();236 console::error("Error: %s\n", title.c_str());237 if (!message.empty()) {238 console::log("%s\n", message.c_str());239 }240 }241 242 static void show_message(const std::string & message) {243 console::log("%s\n", message.c_str());244 }245 246 static void show_info(const std::string & message) {247 console::set_display(DISPLAY_TYPE_INFO);248 console::log("%s\n", message.c_str());249 console::set_display(DISPLAY_TYPE_RESET);250 }251}252 