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