CoolFace
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 2d agoView on Hugging Face
0likes1.1kdownloads
cli-ui.h252 linesDownload Raw Back to cli
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