mirror of
https://github.com/ggml-org/llama.cpp.git
synced 2026-06-29 17:17:40 +02:00
196 lines
6.3 KiB
C++
196 lines
6.3 KiB
C++
#include "arg.h"
|
|
#include "common.h"
|
|
#include "log.h"
|
|
|
|
#include "cli-context.h"
|
|
#include "cli-view.h"
|
|
|
|
#include <array>
|
|
#include <algorithm>
|
|
#include <filesystem>
|
|
#include <string_view>
|
|
#include <signal.h>
|
|
|
|
#if defined(_WIN32)
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#ifndef NOMINMAX
|
|
# define NOMINMAX
|
|
#endif
|
|
#include <windows.h>
|
|
#endif
|
|
|
|
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) || defined (_WIN32)
|
|
static void signal_handler(int) {
|
|
if (g_cli_interrupted.load()) {
|
|
// second Ctrl+C - exit immediately
|
|
// make sure to clear colors before exiting (not using LOG or console.cpp here to avoid deadlock)
|
|
fprintf(stdout, "\033[0m\n");
|
|
fflush(stdout);
|
|
std::exit(130);
|
|
}
|
|
g_cli_interrupted.store(true);
|
|
}
|
|
#endif
|
|
|
|
// TODO?: Make this reusable, enums, docs
|
|
static const std::array<std::string_view, 8> cmds = {
|
|
"/audio ",
|
|
"/clear",
|
|
"/exit",
|
|
"/glob ",
|
|
"/image ",
|
|
"/read ",
|
|
"/regen",
|
|
"/video ",
|
|
};
|
|
|
|
static std::vector<std::pair<std::string, size_t>> auto_completion_callback(std::string_view line, size_t cursor_byte_pos) {
|
|
std::vector<std::pair<std::string, size_t>> matches;
|
|
std::string cmd;
|
|
|
|
if (line.length() > 1 && line.front() == '/' && !std::any_of(cmds.begin(), cmds.end(), [line](std::string_view prefix) {
|
|
return string_starts_with(line, prefix);
|
|
})) {
|
|
auto it = cmds.begin();
|
|
|
|
while ((it = std::find_if(it, cmds.end(), [line](std::string_view cmd_line) {
|
|
return string_starts_with(cmd_line, line);
|
|
})) != cmds.end()) {
|
|
matches.emplace_back(*it, it->length());
|
|
++it;
|
|
}
|
|
} else {
|
|
auto it = std::find_if(cmds.begin(), cmds.end(), [line](std::string_view prefix) {
|
|
return prefix.back() == ' ' && string_starts_with(line, prefix);
|
|
});
|
|
|
|
if (it != cmds.end()) {
|
|
cmd = *it;
|
|
}
|
|
}
|
|
|
|
if (!cmd.empty() && cmd != "/glob " && line.length() >= cmd.length() && cursor_byte_pos >= cmd.length()) {
|
|
const std::string path_prefix = std::string(line.substr(cmd.length(), cursor_byte_pos - cmd.length()));
|
|
const std::string path_postfix = std::string(line.substr(cursor_byte_pos));
|
|
auto cur_dir = std::filesystem::current_path();
|
|
std::string cur_dir_str = cur_dir.string();
|
|
std::string expanded_prefix = path_prefix;
|
|
|
|
#if !defined(_WIN32)
|
|
if (string_starts_with(path_prefix, '~')) {
|
|
const char * home = std::getenv("HOME");
|
|
if (home && home[0]) {
|
|
expanded_prefix = home + path_prefix.substr(1);
|
|
}
|
|
}
|
|
if (string_starts_with(expanded_prefix, '/')) {
|
|
#else
|
|
if (std::isalpha(expanded_prefix[0]) && expanded_prefix.find(':') == 1) {
|
|
#endif
|
|
cur_dir = std::filesystem::path(expanded_prefix).parent_path();
|
|
cur_dir_str.clear();
|
|
} else if (!path_prefix.empty()) {
|
|
cur_dir /= std::filesystem::path(path_prefix).parent_path();
|
|
}
|
|
|
|
std::error_code ec;
|
|
for (const auto & entry : std::filesystem::directory_iterator(cur_dir, ec)) {
|
|
if (ec) {
|
|
break;
|
|
}
|
|
if (!entry.exists(ec)) {
|
|
ec.clear();
|
|
continue;
|
|
}
|
|
|
|
const std::string path_full = entry.path().string();
|
|
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;
|
|
|
|
if (entry.is_directory(ec)) {
|
|
path_entry.push_back(std::filesystem::path::preferred_separator);
|
|
}
|
|
|
|
if (expanded_prefix.empty() || string_starts_with(path_entry, expanded_prefix)) {
|
|
const std::string updated_line = cmd + path_entry;
|
|
matches.emplace_back(updated_line + path_postfix, updated_line.length());
|
|
}
|
|
|
|
if (ec) {
|
|
ec.clear();
|
|
}
|
|
}
|
|
|
|
if (matches.empty()) {
|
|
const std::string updated_line = cmd + path_prefix;
|
|
matches.emplace_back(updated_line + path_postfix, updated_line.length());
|
|
}
|
|
|
|
// Add the longest common prefix
|
|
if (!expanded_prefix.empty() && matches.size() > 1) {
|
|
const std::string_view match0(matches[0].first);
|
|
const std::string_view match1(matches[1].first);
|
|
auto it = std::mismatch(match0.begin(), match0.end(), match1.begin(), match1.end());
|
|
size_t len = it.first - match0.begin();
|
|
|
|
for (size_t i = 2; i < matches.size(); ++i) {
|
|
const std::string_view matchi(matches[i].first);
|
|
auto cmp = std::mismatch(match0.begin(), match0.end(), matchi.begin(), matchi.end());
|
|
len = std::min(len, static_cast<size_t>(cmp.first - match0.begin()));
|
|
}
|
|
|
|
const std::string updated_line = std::string(match0.substr(0, len));
|
|
matches.emplace_back(updated_line + path_postfix, updated_line.length());
|
|
}
|
|
|
|
std::sort(matches.begin(), matches.end(), [](const auto & a, const auto & b) {
|
|
return a.first.compare(0, a.second, b.first, 0, b.second) < 0;
|
|
});
|
|
}
|
|
|
|
return matches;
|
|
}
|
|
|
|
// satisfies -Wmissing-declarations
|
|
int llama_cli(int argc, char ** argv);
|
|
|
|
int llama_cli(int argc, char ** argv) {
|
|
common_params params;
|
|
|
|
params.verbosity = LOG_LEVEL_ERROR; // by default, less verbose logs
|
|
|
|
common_init();
|
|
|
|
if (!common_params_parse(argc, argv, params, LLAMA_EXAMPLE_CLI)) {
|
|
return 1;
|
|
}
|
|
|
|
view::set_completion_callback(auto_completion_callback);
|
|
|
|
#if defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
|
struct sigaction sigint_action;
|
|
sigint_action.sa_handler = signal_handler;
|
|
sigemptyset (&sigint_action.sa_mask);
|
|
sigint_action.sa_flags = 0;
|
|
sigaction(SIGINT, &sigint_action, NULL);
|
|
sigaction(SIGTERM, &sigint_action, NULL);
|
|
#elif defined (_WIN32)
|
|
auto console_ctrl_handler = +[](DWORD ctrl_type) -> BOOL {
|
|
return (ctrl_type == CTRL_C_EVENT) ? (signal_handler(SIGINT), true) : false;
|
|
};
|
|
SetConsoleCtrlHandler(reinterpret_cast<PHANDLER_ROUTINE>(console_ctrl_handler), true);
|
|
#endif
|
|
|
|
cli_context ctx_cli(params);
|
|
|
|
if (!ctx_cli.init()) {
|
|
ctx_cli.shutdown();
|
|
return 1;
|
|
}
|
|
|
|
int ret = ctx_cli.run();
|
|
|
|
ctx_cli.shutdown();
|
|
|
|
return ret;
|
|
}
|