char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
733905474
char/aliceai-t5-moe
1#include "arg.h" 2#include "debug.h" 3#include "log.h" 4#include "common.h" 5#include "sampling.h" 6#include "llama.h" 7#include "ggml.h" 8#include "console.h" 9#include "chat.h" 10#include "mtmd.h" 11#include "mtmd-helper.h" 12 13#include <vector> 14#include <limits.h> 15#include <cinttypes> 16#include <clocale> 17 18#if defined (__unix__ )|| (defined (__APPLE__ )&& defined (__MACH__ )) 19#include <signal.h> 20#include <unistd.h> 21#elif defined (_WIN32 ) 22#define WIN32_LEAN_AND_MEAN 23#ifndef NOMINMAX 24#define NOMINMAX 25#endif 26#include <windows.h> 27#include <signal.h> 28#endif 29 30// volatile, because of signal being an interrupt 31static volatile bool g_is_generating = false; 32static volatile bool g_is_interrupted = false; 33 34/** 35* Please note that this is NOT a production-ready binary. 36* It is a playground for trying multimodal support in llama.cpp. 37* For contributors: please keep this code simple and easy to understand. Do not add unnecessary complexity. The goal is to have a simple CLI for testing multimodal support. 38*/ 39 40static void show_additional_info (int /*argc*/ ,char ** argv ) { 41LOG ( 42"Experimental CLI for multimodal\n\n" 43"Usage: %s [options] -m <model> --mmproj <mmproj> --image <image> --audio <audio> -p <prompt>\n\n" 44" -m and --mmproj are required\n" 45" -hf user/repo can replace both -m and --mmproj in most cases\n" 46" --image, --audio and -p are optional, if NOT provided, the CLI will run in chat mode\n" 47" to disable using GPU for mmproj model, add --no-mmproj-offload\n" , 48argv [0 ] 49 ); 50} 51 52#if defined (__unix__ )|| (defined (__APPLE__ )&& defined (__MACH__ ))|| defined (_WIN32 ) 53static void sigint_handler (int signo ) { 54if (signo == SIGINT ) { 55if (g_is_generating ) { 56g_is_generating = false; 57 }else { 58 console::cleanup (); 59if (g_is_interrupted ) { 60_exit (1 ); 61 } 62g_is_interrupted = true; 63 } 64 } 65} 66#endif 67 68// this is only used by tests.sh to capture the response ; it's not meant to be used in production 69static void inject_test_response_marker () { 70const char * env = std::getenv ("MTMD_TEST_RESPONSE_MARKER" ); 71if (env ) { 72LOG ("%s\n" ,env ); 73 } 74} 75 76struct mtmd_cli_context { 77 mtmd::context_ptr ctx_vision ; 78common_init_result_ptr llama_init ; 79 80llama_model * model ; 81llama_context * lctx ; 82const llama_vocab * vocab ; 83common_sampler * smpl ; 84llama_batch batch ; 85int n_batch ; 86 87 mtmd::bitmaps bitmaps ; 88 std::vector < mtmd_helper::video_ptr > videos ; 89 90mtmd_helper_init_opt init_opt = mtmd_helper_init_opt_default (); 91 std::string video_ffmpeg_bin_dir ; 92 93 mtmd::batch_ptr mbatch ; 94 95// chat template 96common_chat_templates_ptr tmpls ; 97 std::vector < common_chat_msg > chat_history ; 98bool use_jinja = false; 99// TODO: support for --system-prompt with /clear command 100 101// support for legacy templates (models not having EOT token) 102llama_tokens antiprompt_tokens ; 103 104int n_threads = 1 ; 105llama_pos n_past = 0 ; 106 107common_debug_cb_user_data cb_data ; 108 109mtmd_cli_context (common_params & params ) :llama_init (common_init_from_params (params )) { 110model = llama_init -> model (); 111lctx = llama_init -> context (); 112if (!model || !lctx ) { 113exit (1 ); 114 } 115vocab = llama_model_get_vocab (model ); 116smpl = common_sampler_init (model ,params .sampling ); 117n_threads = params .cpuparams .n_threads ; 118batch = llama_batch_init (1 ,0 ,1 );// batch for next token generation 119n_batch = params .n_batch ; 120 121init_vision_context (params ); 122 123if (!mtmd_helper_model_can_chat (lctx ,ctx_vision .get ())) { 124LOG_ERR ("Model does not support chat mode\n" ); 125LOG_ERR ("Hint: for TTS models, please use llama-tts\n" ); 126exit (1 ); 127 } 128 129if (!llama_model_chat_template (model ,nullptr )&& params .chat_template .empty ()) { 130LOG_ERR ("Model does not have chat template.\n" ); 131LOG_ERR (" For old llava models, you may need to use '--chat-template vicuna'\n" ); 132LOG_ERR (" For MobileVLM models, use '--chat-template deepseek'\n" ); 133LOG_ERR (" For Mistral Small 3.1, use '--chat-template mistral-v7'\n" ); 134exit (1 ); 135 } 136 137tmpls = common_chat_templates_init (model ,params .chat_template ); 138use_jinja = params .use_jinja ; 139chat_history .clear (); 140LOG_INF ("%s: chat template example:\n%s\n" ,__func__ ,common_chat_format_example (tmpls .get (),params .use_jinja ,params .default_template_kwargs ).c_str ()); 141 142// load antiprompt tokens for legacy templates 143if (params .chat_template == "vicuna" ) { 144antiprompt_tokens = common_tokenize (lctx ,"ASSISTANT:" , false, true); 145 }else if (params .chat_template == "deepseek" ) { 146antiprompt_tokens = common_tokenize (lctx ,"###" , false, true); 147 } 148 } 149 150 ~mtmd_cli_context () { 151llama_batch_free (batch ); 152common_sampler_free (smpl ); 153 } 154 155void init_vision_context (common_params & params ) { 156const char * clip_path = params .mmproj .path .c_str (); 157mtmd_context_params mparams = mtmd_context_params_default (); 158mparams .use_gpu = params .mmproj_use_gpu ; 159mparams .device = params .mmproj_device ; 160mparams .print_timings = true; 161mparams .n_threads = params .cpuparams .n_threads ; 162mparams .flash_attn_type = params .flash_attn_type ; 163mparams .warmup = params .warmup ; 164mparams .image_min_tokens = params .image_min_tokens ; 165mparams .image_max_tokens = params .image_max_tokens ; 166if (std::getenv ("MTMD_DEBUG_GRAPH" )!= nullptr ) { 167mparams .cb_eval_user_data = & cb_data ; 168mparams .cb_eval = common_debug_cb_eval ; 169 } 170ctx_vision .reset (mtmd_init_from_file (clip_path ,model ,mparams )); 171if (!ctx_vision .get ()) { 172LOG_ERR ("Failed to load vision model from %s\n" ,clip_path ); 173exit (1 ); 174 } 175 176video_ffmpeg_bin_dir = params .video_ffmpeg_bin_dir ; 177init_opt .video_params .fps_target = params .video_fps ; 178init_opt .video_params .timestamp_interval_ms = params .video_timestamp_interval_ms ; 179init_opt .video_params .ffmpeg_bin_dir = video_ffmpeg_bin_dir .empty () 180 ?nullptr :video_ffmpeg_bin_dir .c_str (); 181 } 182 183bool check_antiprompt (const llama_tokens & generated_tokens ) { 184if (antiprompt_tokens .empty ()|| generated_tokens .size ()< antiprompt_tokens .size ()) { 185return false; 186 } 187return std::equal ( 188generated_tokens .end ()- antiprompt_tokens .size (), 189generated_tokens .end (), 190antiprompt_tokens .begin () 191 ); 192 } 193 194bool load_media (const std::string & fname ) { 195auto res = mtmd_helper_bitmap_init_from_file (ctx_vision .get (),fname .c_str (), false,init_opt ); 196if (!res .bitmap ) { 197return false; 198 } 199bitmaps .entries .emplace_back (res .bitmap ); 200if (res .video_ctx ) { 201videos .emplace_back (res .video_ctx ); 202 } 203return true; 204 } 205}; 206 207static int generate_response (mtmd_cli_context & ctx ,int n_predict ) { 208llama_tokens generated_tokens ; 209for (int i = 0 ;i < n_predict ;i ++ ) { 210if (i > n_predict || !g_is_generating || g_is_interrupted ) { 211LOG ("\n" ); 212break ; 213 } 214 215llama_token token_id = common_sampler_sample (ctx .smpl ,ctx .lctx ,-1 ); 216generated_tokens .push_back (token_id ); 217common_sampler_accept (ctx .smpl ,token_id , true); 218 219if (llama_vocab_is_eog (ctx .vocab ,token_id )|| ctx .check_antiprompt (generated_tokens )) { 220LOG ("\n" ); 221break ;// end of generation 222 } 223 224LOG ("%s" ,common_token_to_piece (ctx .lctx ,token_id ).c_str ()); 225fflush (stdout ); 226 227if (g_is_interrupted ) { 228LOG ("\n" ); 229break ; 230 } 231 232// eval the token 233common_batch_clear (ctx .batch ); 234common_batch_add (ctx .batch ,token_id ,ctx .n_past ++ , {0 }, true); 235if (llama_decode (ctx .lctx ,ctx .batch )) { 236LOG_ERR ("failed to decode token\n" ); 237return 1 ; 238 } 239 } 240 241 std::string generated_text = common_detokenize (ctx .lctx ,generated_tokens ); 242common_chat_msg msg ; 243msg .role = "assistant" ; 244msg .content = generated_text ; 245ctx .chat_history .push_back (std::move (msg )); 246 247return 0 ; 248} 249 250static std::string chat_add_and_format (mtmd_cli_context & ctx ,common_chat_msg & new_msg ) { 251LOG_DBG ("chat_add_and_format: new_msg.role='%s', new_msg.content='%s'\n" , 252new_msg .role .c_str (),new_msg .content .c_str ()); 253auto formatted = common_chat_format_single (ctx .tmpls .get (),ctx .chat_history , 254new_msg ,new_msg .role == "user" , 255ctx .use_jinja ); 256ctx .chat_history .push_back (new_msg ); 257return formatted ; 258} 259 260static int eval_message (mtmd_cli_context & ctx ,common_chat_msg & msg ) { 261inject_test_response_marker (); 262 263bool add_bos = ctx .chat_history .empty (); 264auto formatted_chat = chat_add_and_format (ctx ,msg ); 265LOG_DBG ("formatted_chat.prompt: %s\n" ,formatted_chat .c_str ()); 266 267if (g_is_interrupted )return 0 ; 268 269// note: we replace the marker here instead of letting mtmd_tokenize() to do that 270// because we want to demonstrate how to use mtmd_tokenize_from_parts() 271 272// split the formatted chat on the media marker to get text segments 273const std::string marker = mtmd_default_marker (); 274 std::vector < std::string > segments ; 275size_t start = 0 ; 276size_t pos ; 277while ((pos = formatted_chat .find (marker ,start ))!= std::string::npos ) { 278segments .push_back (formatted_chat .substr (start ,pos - start )); 279start = pos + marker .size (); 280 } 281segments .push_back (formatted_chat .substr (start )); 282 283auto bitmaps_c_ptr = ctx .bitmaps .c_ptr (); 284if (segments .size ()- 1 != bitmaps_c_ptr .size ()) { 285LOG_ERR ("Number of media markers (%zu) does not match number of loaded media (%zu)\n" , 286segments .size ()- 1 ,bitmaps_c_ptr .size ()); 287return 1 ; 288 } 289 290// interleave text and media parts 291 std::vector < mtmd_input_text > texts (segments .size ()); 292 std::vector < mtmd_input_part > parts ; 293for (size_t i = 0 ;i < segments .size ();i ++ ) { 294texts [i ]= {segments [i ].data (),segments [i ].size (),/* add_special */ false,/* parse_special */ true}; 295parts .push_back ({& texts [i ],nullptr }); 296if (i < bitmaps_c_ptr .size ()) { 297parts .push_back ({nullptr ,bitmaps_c_ptr [i ]}); 298 } 299 } 300 std::vector < const mtmd_input_part *> parts_ptr ; 301for (const auto & p :parts ) { 302parts_ptr .push_back (& p ); 303 } 304 305 mtmd::input_chunks chunks (mtmd_input_chunks_init ()); 306int32_t res = mtmd_tokenize_from_parts (ctx .ctx_vision .get (), 307chunks .ptr .get (),// output 308parts_ptr .data (), 309parts_ptr .size (), 310add_bos ); 311if (res != 0 ) { 312LOG_ERR ("Unable to tokenize prompt, res = %d\n" ,res ); 313return 1 ; 314 } 315 316ctx .bitmaps .entries .clear (); 317ctx .videos .clear (); 318 319// batch encode all media chunks, then decode each 320size_t n_chunks = mtmd_input_chunks_size (chunks .ptr .get ()); 321for (size_t i = 0 ;i < n_chunks ;i ++ ) { 322auto chunk = mtmd_input_chunks_get (chunks .ptr .get (),i ); 323auto chunk_type = mtmd_input_chunk_get_type (chunk ); 324 325if (chunk_type == MTMD_INPUT_CHUNK_TYPE_TEXT ) { 326// decode text chunk 327llama_pos new_n_past = ctx .n_past ; 328res = mtmd_helper_eval_chunk_single (ctx .ctx_vision .get (), 329ctx .lctx , 330chunk , 331ctx .n_past , 3320 ,// seq_id 333ctx .n_batch , 334i == n_chunks - 1 ,// logits_last 335& new_n_past ); 336if (res != 0 ) { 337LOG_ERR ("Unable to eval text chunk %zu\n" ,i ); 338return 1 ; 339 } 340ctx .n_past = new_n_past ; 341 }else { 342// media chunk: try to get embd from existing batch, or create a new batch 343float * embd = nullptr ; 344if (ctx .mbatch ) { 345embd = mtmd_batch_get_output_embd (ctx .mbatch .get (),chunk ); 346 347if (embd ) { 348LOG_DBG ("found embd for media chunk %zu in existing batch\n" ,i ); 349 }else { 350LOG_DBG ("media chunk %zu not found in existing batch, creating new batch\n" ,i ); 351 } 352 } 353 354if (!embd ) { 355// create and encode a new batch with as many media chunks as possible 356ctx .mbatch .reset (mtmd_batch_init (ctx .ctx_vision .get ())); 357res = mtmd_batch_add_chunk (ctx .mbatch .get (),chunk ); 358GGML_ASSERT (res == 0 );// first chunk must always succeed 359 360int n_added = 1 ; 361// add as many subsequent media chunks as possible 362for (size_t j = i + 1 ;j < n_chunks ;j ++ ) { 363auto next_chunk = mtmd_input_chunks_get (chunks .ptr .get (),j ); 364auto next_type = mtmd_input_chunk_get_type (next_chunk ); 365if (next_type == MTMD_INPUT_CHUNK_TYPE_TEXT ) { 366break ;// text chunk splits the batch 367 } 368res = mtmd_batch_add_chunk (ctx .mbatch .get (),next_chunk ); 369if (res != 0 ) { 370break ;// batch full or incompatible 371 } 372n_added ++ ; 373 } 374 375int64_t time_start = ggml_time_ms (); 376LOG_INF ("encoding mtmd batch, n_chunks = %d (done = %zu, total = %zu)\n" ,n_added ,i ,n_chunks ); 377res = mtmd_batch_encode (ctx .mbatch .get ()); 378if (res != 0 ) { 379LOG_ERR ("Failed to encode mtmd batch, res = %d\n" ,res ); 380return 1 ; 381 } 382LOG_INF ("mtmd batch encoding done in %d ms\n" , (int )(ggml_time_ms ()- time_start )); 383 384embd = mtmd_batch_get_output_embd (ctx .mbatch .get (),chunk ); 385 } 386 387GGML_ASSERT (embd != nullptr ); 388 389llama_pos new_n_past = ctx .n_past ; 390res = mtmd_helper_decode_image_chunk (ctx .ctx_vision .get (), 391ctx .lctx , 392chunk , 393embd , 394ctx .n_past , 3950 ,// seq_id 396ctx .n_batch , 397& new_n_past , 398nullptr ,// callback 399nullptr // user_data 400 ); 401if (res != 0 ) { 402LOG_ERR ("Unable to decode media chunk %zu\n" ,i ); 403return 1 ; 404 } 405ctx .n_past = new_n_past ; 406 } 407 } 408 409LOG ("\n" ); 410 411return 0 ; 412} 413 414int main (int argc ,char ** argv ) { 415 std::setlocale (LC_NUMERIC ,"C" ); 416 417ggml_time_init (); 418 419common_params params ; 420 421common_init (); 422 423if (!common_params_parse (argc ,argv ,params ,LLAMA_EXAMPLE_MTMD ,show_additional_info )) { 424return 1 ; 425 } 426 427mtmd_helper_log_set (common_log_default_callback ,nullptr ); 428 429if (params .mmproj .path .empty ()) { 430show_additional_info (argc ,argv ); 431LOG_ERR ("ERR: Missing --mmproj argument\n" ); 432return 1 ; 433 } 434 435ggml_backend_load_all (); 436 437mtmd_cli_context ctx (params ); 438LOG_INF ("%s: loading model: %s\n" ,__func__ ,params .model .path .c_str ()); 439 440bool is_single_turn = !params .prompt .empty ()&& !params .image .empty (); 441 442int n_predict = params .n_predict < 0 ?INT_MAX :params .n_predict ; 443 444 console::init (params .simple_io ,params .use_color ); 445atexit ([]() { console::cleanup (); }); 446 447// Ctrl+C handling 448 { 449#if defined (__unix__ )|| (defined (__APPLE__ )&& defined (__MACH__ )) 450struct sigaction sigint_action ; 451sigint_action .sa_handler = sigint_handler ; 452sigemptyset (& sigint_action .sa_mask ); 453sigint_action .sa_flags = 0 ; 454sigaction (SIGINT ,& sigint_action ,NULL ); 455#elif defined (_WIN32 ) 456auto console_ctrl_handler = + [](DWORD ctrl_type )-> BOOL { 457return (ctrl_type == CTRL_C_EVENT ) ? (sigint_handler (SIGINT ), true) : false; 458 }; 459SetConsoleCtrlHandler (reinterpret_cast < PHANDLER_ROUTINE > (console_ctrl_handler ), true); 460#endif 461 } 462 463if (g_is_interrupted )return 130 ; 464 465auto eval_system_prompt_if_present = [& ] { 466if (params .system_prompt .empty ()) { 467return 0 ; 468 } 469 470common_chat_msg msg ; 471msg .role = "system" ; 472msg .content = params .system_prompt ; 473return eval_message (ctx ,msg ); 474 }; 475 476LOG_WRN ("WARN: This is an experimental CLI for testing multimodal capability.\n" ); 477LOG_WRN (" For normal use cases, please use the standard llama-cli\n" ); 478 479if (eval_system_prompt_if_present ()) { 480return 1 ; 481 } 482 483if (is_single_turn ) { 484g_is_generating = true; 485if (params .prompt .find (mtmd_default_marker ())== std::string::npos ) { 486for (size_t i = 0 ;i < params .image .size ();i ++ ) { 487// most models require the marker before each image 488// ref: https://github.com/ggml-org/llama.cpp/pull/17616 489params .prompt = mtmd_default_marker ()+ params .prompt ; 490 } 491 } 492 493common_chat_msg msg ; 494msg .role = "user" ; 495msg .content = params .prompt ; 496for (const auto & image :params .image ) { 497if (!ctx .load_media (image )) { 498return 1 ;// error is already printed by libmtmd 499 } 500 } 501if (eval_message (ctx ,msg )) { 502return 1 ; 503 } 504if (!g_is_interrupted && generate_response (ctx ,n_predict )) { 505return 1 ; 506 } 507 508 }else { 509LOG ("\n Running in chat mode, available commands:" ); 510if (mtmd_support_vision (ctx .ctx_vision .get ())) { 511LOG ("\n /image <path> load an image" ); 512 } 513if (mtmd_support_audio (ctx .ctx_vision .get ())) { 514LOG ("\n /audio <path> load an audio" ); 515 } 516if (mtmd_helper_support_video (ctx .ctx_vision .get ())) { 517LOG ("\n /video <path> load a video" ); 518 } 519LOG ("\n /clear clear the chat history" ); 520LOG ("\n /quit or /exit exit the program" ); 521LOG ("\n" ); 522 523 std::string content ; 524 525while (!g_is_interrupted ) { 526g_is_generating = false; 527LOG ("\n> " ); 528 console::set_display (DISPLAY_TYPE_USER_INPUT ); 529 std::string line ; 530 console::readline (line , false); 531if (g_is_interrupted )break ; 532 console::set_display (DISPLAY_TYPE_RESET ); 533line = string_strip (line ); 534if (line .empty ()) { 535continue ; 536 } 537if (line == "/quit" || line == "/exit" ) { 538break ; 539 } 540if (line == "/clear" ) { 541ctx .n_past = 0 ; 542ctx .chat_history .clear (); 543llama_memory_clear (llama_get_memory (ctx .lctx ), true); 544if (eval_system_prompt_if_present ()) { 545return 1 ; 546 } 547LOG ("Chat history cleared\n\n" ); 548continue ; 549 } 550g_is_generating = true; 551bool is_image = line == "/image" || line .find ("/image " )== 0 ; 552bool is_audio = line == "/audio" || line .find ("/audio " )== 0 ; 553bool is_video = line == "/video" || line .find ("/video " )== 0 ; 554if (is_image || is_audio || is_video ) { 555if (line .size ()< 8 ) { 556LOG_ERR ("ERR: Missing media filename\n" ); 557continue ; 558 } 559 std::string media_path = line .substr (7 ); 560if (ctx .load_media (media_path )) { 561LOG ("%s %s loaded\n" ,media_path .c_str (),is_image ?"image" :is_audio ?"audio" :"video" ); 562content += mtmd_default_marker (); 563 } 564// else, error is already printed by libmtmd 565continue ; 566 }else { 567content += line ; 568 } 569common_chat_msg msg ; 570msg .role = "user" ; 571msg .content = content ; 572int ret = eval_message (ctx ,msg ); 573if (ret ) { 574return 1 ; 575 } 576if (g_is_interrupted )break ; 577if (generate_response (ctx ,n_predict )) { 578return 1 ; 579 } 580content .clear (); 581 } 582 } 583if (g_is_interrupted )LOG ("\nInterrupted by user\n" ); 584LOG ("\n\n" ); 585llama_perf_context_print (ctx .lctx ); 586return g_is_interrupted ?130 :0 ; 587}