char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
18443257a
char/aliceai-t5-moe
1#include "server-context.h" 2#include "server-http.h" 3#include "server-models.h" 4#include "server-cors-proxy.h" 5#include "server-stream.h" 6#include "server-tools.h" 7 8#include "arg.h" 9#include "build-info.h" 10#include "common.h" 11#include "fit.h" 12#include "llama.h" 13#include "log.h" 14 15#include <atomic> 16#include <clocale> 17#include <exception> 18#include <signal.h> 19#include <thread> // for std::thread::hardware_concurrency 20 21#if defined(_WIN32 ) 22#include <windows.h> 23#endif 24 25static std::function < void (int )> shutdown_handler ; 26static std::atomic_flag is_terminating = ATOMIC_FLAG_INIT ; 27 28static inline void signal_handler (int signal ) { 29if (is_terminating .test_and_set ()) { 30// in case it hangs, we can force terminate the server by hitting Ctrl+C twice 31// this is for better developer experience, we can remove when the server is stable enough 32fprintf (stderr ,"Received second interrupt, terminating immediately.\n" ); 33exit (1 ); 34 } 35 36shutdown_handler (signal ); 37} 38 39// satisfies -Wmissing-declarations (used by llama command) 40int llama_server (int argc ,char ** argv ); 41 42// to be used via CLI (argc / argv are used by router mode only) 43int llama_server (common_params & params ,int argc ,char ** argv ); 44void llama_server_terminate (); 45void llama_server_terminate () { 46if (shutdown_handler ) { 47shutdown_handler (0 ); 48 } 49} 50 51 52// wrapper function that handles exceptions and logs errors 53// this is to make sure handler_t never throws exceptions; instead, it returns an error response 54static server_http_context::handler_t ex_wrapper (server_http_context::handler_t func ) { 55return [func = std::move (func )](const server_http_req & req )-> server_http_res_ptr { 56 std::string message ; 57error_type error ; 58try { 59return func (req ); 60 }catch (const std::invalid_argument & e ) { 61// treat invalid_argument as invalid request (400) 62error = ERROR_TYPE_INVALID_REQUEST ; 63message = e .what (); 64 }catch (const std::exception & e ) { 65// treat other exceptions as server error (500) 66error = ERROR_TYPE_SERVER ; 67message = e .what (); 68 }catch (...) { 69error = ERROR_TYPE_SERVER ; 70message = "unknown error" ; 71 } 72 73auto res = std::make_unique < server_http_res > (); 74res -> status = 500 ; 75try { 76json error_data = format_error_response (message ,error ); 77res -> status = json_value (error_data ,"code" ,500 ); 78res -> data = safe_json_to_str ({{"error" ,error_data }}); 79SRV_WRN ("got exception: %s\n" ,res -> data .c_str ()); 80 }catch (const std::exception & e ) { 81SRV_ERR ("got another exception: %s | while handling exception: %s\n" ,e .what (),message .c_str ()); 82res -> data = "Internal Server Error" ; 83 } 84return res ; 85 }; 86} 87 88int llama_server (int argc ,char ** argv ) { 89 std::setlocale (LC_NUMERIC ,"C" ); 90 91#ifndef _WIN32 92// Ignore SIGPIPE so the server does not crash if a child (MCP server, tools runtime) exits while we are writing to its stdin 93signal (SIGPIPE ,SIG_IGN ); 94#endif 95 96// own arguments required by this example 97common_params params ; 98 99common_init (); 100 101// start the stream session manager GC right after common init, before any HTTP route can 102// touch it. lifecycle is symmetric, stop_gc() runs in clean_up() before backend free 103server_stream_session_manager_start (); 104 105if (!common_params_parse (argc ,argv ,params ,LLAMA_EXAMPLE_SERVER )) { 106return 1 ; 107 } 108 109llama_backend_init (); 110llama_numa_init (params .numa ); 111 112return llama_server (params ,argc ,argv ); 113} 114 115int llama_server (common_params & params ,int argc ,char ** argv ) { 116bool is_run_by_cli = (argv == nullptr ); 117 118common_models_handler models_handler ; 119 120// note: router mode also accepts -hf remote-preset, so we need to check that first 121if (!is_run_by_cli && !params .model .hf_repo .empty ()) { 122try { 123models_handler = common_models_handler_init (params ,LLAMA_EXAMPLE_SERVER ); 124if (common_models_handler_is_preset_repo (models_handler )) { 125// apply the preset and start the server in router mode 126common_models_handler_apply (models_handler ,params ); 127 } 128 }catch (const std::exception & e ) { 129SRV_ERR ("failed to fetch model metadata: %s\n" ,e .what ()); 130return 1 ; 131 } 132 } 133 134// router server never loads a model and must not touch the GPU 135const bool is_router_server = params .model .path .empty () 136&& params .model .hf_repo .empty () 137&& params .model .docker_repo .empty (); 138 139// skip device enumeration so the CUDA primary context stays uncreated 140common_params_print_info (params , !is_router_server ); 141 142if (!is_router_server ) { 143// validate batch size for embeddings 144// embeddings require all tokens to be processed in a single ubatch 145// see https://github.com/ggml-org/llama.cpp/issues/12836 146if (params .embedding && params .n_batch > params .n_ubatch ) { 147SRV_WRN ("embeddings enabled with n_batch (%d) > n_ubatch (%d)\n" ,params .n_batch ,params .n_ubatch ); 148SRV_WRN ("setting n_batch = n_ubatch = %d to avoid assertion failure\n" ,params .n_ubatch ); 149params .n_batch = params .n_ubatch ; 150 } 151 152if (params .n_parallel < 0 ) { 153SRV_TRC ("%s" ,"n_parallel is set to auto, using n_parallel = 4 and kv_unified = true\n" ); 154 155params .n_parallel = 4 ; 156params .kv_unified = true; 157 } 158 } 159 160// size the KV pool from --kv-unified-per-slot, unless the user pinned it with -c 161// or with -c 0 for max context 162const bool ctx_pool_auto_sized = params .kv_unified_per_slot > 0 && 163params .n_ctx == 0 && 164 (uint32_t )params .fit_params_min_ctx != UINT32_MAX ; 165 166if (ctx_pool_auto_sized ) { 167params .n_ctx = params .n_parallel * params .kv_unified_per_slot ; 168SRV_INF ("--kv-unified-per-slot: sizing KV pool to n_parallel * kv_unified_per_slot = %d * %d = %d\n" ,params .n_parallel , 169params .kv_unified_per_slot ,params .n_ctx ); 170 } 171 172// for consistency between server router mode and single-model mode, we set the same model name as alias 173auto model_name = params .model .get_name (); 174if (params .model_alias .empty ()&& !model_name .empty ()) { 175params .model_alias .insert (model_name ); 176 } 177 178// note: this is guaranteed to out-live ctx_http and tools 179server_mcp mcp_mgr ; 180 181// struct that contains llama context and inference 182server_context ctx_server ; 183 184server_http_context ctx_http ; 185if (!ctx_http .init (params )) { 186SRV_ERR ("%s" ,"failed to initialize HTTP server\n" ); 187return 1 ; 188 } 189 190// 191// Router 192// 193 194// register API routes 195server_child child ;// only used in non-router mode 196server_routes routes (params ,ctx_server ); 197server_tools tools ; 198 199 std::optional < server_models_routes > models_routes {}; 200if (is_router_server ) { 201// setup server instances manager 202try { 203models_routes .emplace (params ,argc ,argv ); 204 }catch (const std::exception & e ) { 205SRV_ERR ("failed to initialize router models: %s\n" ,e .what ()); 206return 1 ; 207 } 208 209// proxy handlers 210// note: routes.get_health stays the same 211routes .get_metrics = models_routes -> proxy_get ; 212routes .post_props = models_routes -> proxy_post ; 213routes .post_completions = models_routes -> proxy_post ; 214routes .post_completions_oai = models_routes -> proxy_post ; 215routes .post_chat_completions = models_routes -> proxy_post ; 216routes .post_control = models_routes -> proxy_post ; 217routes .post_responses_oai = models_routes -> proxy_post ; 218routes .post_transcriptions_oai = models_routes -> proxy_post ; 219routes .post_anthropic_messages = models_routes -> proxy_post ; 220routes .post_anthropic_count_tokens = models_routes -> proxy_post ; 221routes .post_infill = models_routes -> proxy_post ; 222routes .post_embeddings = models_routes -> proxy_post ; 223routes .post_embeddings_oai = models_routes -> proxy_post ; 224routes .post_rerank = models_routes -> proxy_post ; 225routes .post_tokenize = models_routes -> proxy_post ; 226routes .post_detokenize = models_routes -> proxy_post ; 227routes .post_apply_template = models_routes -> proxy_post ; 228routes .post_chat_completions_tok = models_routes -> proxy_post ; 229routes .post_responses_tok_oai = models_routes -> proxy_post ; 230routes .get_lora_adapters = models_routes -> proxy_get ; 231routes .post_lora_adapters = models_routes -> proxy_post ; 232routes .get_slots = models_routes -> proxy_get ; 233routes .post_slots = models_routes -> proxy_post ; 234 235// custom routes for router 236routes .get_props = models_routes -> get_router_props ; 237routes .get_models = models_routes -> get_router_models ; 238 239ctx_http .post ("/models" ,ex_wrapper (models_routes -> post_router_models )); 240ctx_http .post ("/models/load" ,ex_wrapper (models_routes -> post_router_models_load )); 241ctx_http .post ("/models/unload" ,ex_wrapper (models_routes -> post_router_models_unload )); 242ctx_http .get ("/models/sse" ,ex_wrapper (models_routes -> get_router_models_sse )); 243ctx_http .del ("/models" ,ex_wrapper (models_routes -> del_router_models )); 244 } 245 246ctx_http .get ("/health" ,ex_wrapper (routes .get_health ));// public endpoint (no API key check) 247ctx_http .get ("/v1/health" ,ex_wrapper (routes .get_health ));// public endpoint (no API key check) 248ctx_http .get ("/metrics" ,ex_wrapper (routes .get_metrics )); 249ctx_http .get ("/props" ,ex_wrapper (routes .get_props )); 250ctx_http .post ("/props" ,ex_wrapper (routes .post_props )); 251ctx_http .get ("/models" ,ex_wrapper (routes .get_models )); 252ctx_http .get ("/v1/models" ,ex_wrapper (routes .get_models )); 253ctx_http .post ("/completion" ,ex_wrapper (routes .post_completions ));// legacy 254ctx_http .post ("/completions" ,ex_wrapper (routes .post_completions )); 255ctx_http .post ("/v1/completions" ,ex_wrapper (routes .post_completions_oai )); 256ctx_http .post ("/chat/completions" ,ex_wrapper (routes .post_chat_completions )); 257ctx_http .post ("/v1/chat/completions" ,ex_wrapper (routes .post_chat_completions )); 258ctx_http .post ("/v1/chat/completions/control" ,ex_wrapper (routes .post_control )); 259ctx_http .post ("/v1/responses" ,ex_wrapper (routes .post_responses_oai )); 260ctx_http .post ("/responses" ,ex_wrapper (routes .post_responses_oai )); 261ctx_http .post ("/v1/audio/transcriptions" ,ex_wrapper (routes .post_transcriptions_oai )); 262ctx_http .post ("/audio/transcriptions" ,ex_wrapper (routes .post_transcriptions_oai )); 263ctx_http .post ("/v1/messages" ,ex_wrapper (routes .post_anthropic_messages ));// anthropic messages API 264ctx_http .post ("/infill" ,ex_wrapper (routes .post_infill )); 265ctx_http .post ("/embedding" ,ex_wrapper (routes .post_embeddings ));// legacy 266ctx_http .post ("/embeddings" ,ex_wrapper (routes .post_embeddings )); 267ctx_http .post ("/v1/embeddings" ,ex_wrapper (routes .post_embeddings_oai )); 268ctx_http .post ("/rerank" ,ex_wrapper (routes .post_rerank )); 269ctx_http .post ("/reranking" ,ex_wrapper (routes .post_rerank )); 270ctx_http .post ("/v1/rerank" ,ex_wrapper (routes .post_rerank )); 271ctx_http .post ("/v1/reranking" ,ex_wrapper (routes .post_rerank )); 272ctx_http .post ("/tokenize" ,ex_wrapper (routes .post_tokenize )); 273ctx_http .post ("/detokenize" ,ex_wrapper (routes .post_detokenize )); 274ctx_http .post ("/apply-template" ,ex_wrapper (routes .post_apply_template )); 275// token counting 276ctx_http .post ("/chat/completions/input_tokens" ,ex_wrapper (routes .post_chat_completions_tok )); 277ctx_http .post ("/v1/chat/completions/input_tokens" ,ex_wrapper (routes .post_chat_completions_tok )); 278ctx_http .post ("/responses/input_tokens" ,ex_wrapper (routes .post_responses_tok_oai )); 279ctx_http .post ("/v1/responses/input_tokens" ,ex_wrapper (routes .post_responses_tok_oai )); 280ctx_http .post ("/v1/messages/count_tokens" ,ex_wrapper (routes .post_anthropic_count_tokens ));// anthropic token counting 281// LoRA adapters hotswap 282ctx_http .get ("/lora-adapters" ,ex_wrapper (routes .get_lora_adapters )); 283ctx_http .post ("/lora-adapters" ,ex_wrapper (routes .post_lora_adapters )); 284// Save & load slots 285ctx_http .get ("/slots" ,ex_wrapper (routes .get_slots )); 286ctx_http .post ("/slots/:id_slot" ,ex_wrapper (routes .post_slots )); 287 288// resumable streaming: a child binds the local session factories, the router binds 289// proxies that resolve the owning child, see server-stream.h 290 server_http_context::handler_t stream_get_h ; 291 server_http_context::handler_t streams_lookup_h ; 292 server_http_context::handler_t stream_delete_h ; 293if (is_router_server ) { 294stream_get_h = models_routes -> router_stream_get ; 295streams_lookup_h = models_routes -> router_streams_lookup ; 296stream_delete_h = models_routes -> router_stream_delete ; 297 }else { 298stream_get_h = server_stream_make_get_handler (); 299streams_lookup_h = server_stream_make_lookup_handler (); 300stream_delete_h = server_stream_make_delete_handler (); 301 } 302ctx_http .get ("/v1/stream" ,ex_wrapper (stream_get_h )); 303ctx_http .post ("/v1/streams/lookup" ,ex_wrapper (streams_lookup_h )); 304ctx_http .del ("/v1/stream" ,ex_wrapper (stream_delete_h )); 305 306// Google Cloud Platform (Vertex AI) compat 307ctx_http .register_gcp_compat (); 308 309// return 403 for disabled features 310 server_http_context::handler_t res_403 = [](const server_http_req & ) { 311auto res = std::make_unique < server_http_res > (); 312res -> status = 403 ; 313res -> data = safe_json_to_str ({ 314 {"error" , { 315 {"message" ,"this feature is disabled" }, 316 {"type" ,"feature_disabled" }, 317 }} 318 }); 319return res ; 320 }; 321 322if (params .cors_origins == "*" && params .api_keys .empty ()) { 323SRV_WRN ("%s" ,"-----------------\n" ); 324SRV_WRN ("%s" ,"CORS is set to allow all origins ('*') and no API key is set\n" ); 325SRV_WRN ("%s" ,"this can be a security risk (cross-origin attacks)\n" ); 326SRV_WRN ("%s" ,"more info: https://github.com/ggml-org/llama.cpp/pull/25655\n" ); 327SRV_WRN ("%s" ,"-----------------\n" ); 328 } 329 330// CORS proxy (EXPERIMENTAL, only used by the Web UI for MCP) 331 std::vector < std::string > warn_names ; 332if (is_router_server ) { 333warn_names .push_back ("router mode" ); 334 } 335 336if (params .ui_mcp_proxy ) { 337ctx_http .get ("/cors-proxy" ,ex_wrapper (proxy_handler_get )); 338ctx_http .post ("/cors-proxy" ,ex_wrapper (proxy_handler_post )); 339warn_names .push_back ("MCP proxy (experimental)" ); 340 }else { 341ctx_http .get ("/cors-proxy" ,ex_wrapper (res_403 )); 342ctx_http .post ("/cors-proxy" ,ex_wrapper (res_403 )); 343 } 344 345try { 346mcp_mgr .start (params ); 347 }catch (const std::exception & e ) { 348SRV_ERR ("MCP starting failed: %s\n" ,e .what ()); 349return 1 ; 350 } 351 352if (!params .server_tools .empty ()|| !mcp_mgr .empty ()) { 353try { 354tools .setup (params .server_tools ,mcp_mgr ,params .server_tools_runtime ); 355 }catch (const std::exception & e ) { 356SRV_ERR ("tools setup failed: %s\n" ,e .what ()); 357return 1 ; 358 } 359ctx_http .get ("/tools" ,ex_wrapper (tools .handle_get )); 360ctx_http .post ("/tools" ,ex_wrapper (tools .handle_post )); 361if (!params .server_tools .empty ()) { 362warn_names .push_back ("server tools (experimental)" ); 363 } 364if (!params .server_tools_runtime .empty ()) { 365warn_names .push_back ("tools runtime (experimental)" ); 366 } 367if (!mcp_mgr .empty ()) { 368warn_names .push_back ("MCP servers (experimental)" ); 369 } 370 }else { 371ctx_http .get ("/tools" ,ex_wrapper (res_403 )); 372ctx_http .post ("/tools" ,ex_wrapper (res_403 )); 373 } 374 375if (warn_names .size ()> 0 ) { 376SRV_WRN ("%s" ,"-----------------\n" ); 377SRV_WRN ("%s" ,"the following feature(s) are enabled:\n" ); 378for (const auto & name :warn_names ) { 379SRV_WRN (" %s\n" ,name .c_str ()); 380 } 381SRV_WRN ("%s" ,"do not expose the server to untrusted environments\n" ); 382SRV_WRN ("%s" ,"-----------------\n" ); 383 } 384 385// 386// Handle downloading model 387// 388 389if (child .is_child ()&& child .get_mode ()== SERVER_CHILD_MODE_DOWNLOAD ) { 390return child .run_download (params ); 391 }else if (!is_router_server && !is_run_by_cli ) { 392// single-model mode (NOT spawned by router) 393// if this is invoked by CLI, model downloading should be already handled 394try { 395common_models_handler_apply (models_handler ,params ); 396 }catch (const std::exception & e ) { 397SRV_ERR ("failed to download model: %s\n" ,e .what ()); 398return 1 ; 399 } 400 } 401 402// 403// Start the server 404// 405 406 std::function < void ()> clean_up ; 407 408if (is_router_server ) { 409SRV_INF ("%s" ,"starting server in router mode. models will be automatically loaded on-demand\n" ); 410 411clean_up = [& models_routes ,& mcp_mgr ]() { 412SRV_INF ("%s: cleaning up before exit...\n" ,__func__ ); 413// stop the session GC first, it finalizes live sessions and wakes pending readers 414server_stream_session_manager_stop (); 415if (models_routes .has_value ()) { 416models_routes -> stopping .store (true);// maybe redundant, but just to be safe 417models_routes -> models .unload_all (); 418 } 419mcp_mgr .shutdown (); 420llama_backend_free (); 421 }; 422 423if (!ctx_http .start ()) { 424clean_up (); 425SRV_ERR ("%s" ,"exiting due to HTTP server error\n" ); 426return 1 ; 427 } 428ctx_http .is_ready .store (true); 429 430shutdown_handler = [& ](int ) { 431if (models_routes .has_value ()) { 432// important to disconnect any SSE clients 433models_routes -> stopping .store (true); 434 } 435mcp_mgr .shutdown (); 436ctx_http .stop (); 437 }; 438 439try { 440models_routes -> models .load_startup_models (); 441 }catch (const std::exception & e ) { 442SRV_ERR ("failed to load models on startup: %s\n" ,e .what ()); 443ctx_http .stop (); 444if (ctx_http .thread .joinable ()) { 445ctx_http .thread .join (); 446 } 447clean_up (); 448return 1 ; 449 } 450 451 }else { 452// setup clean up function, to be called before exit 453clean_up = [& ctx_http ,& ctx_server ,& mcp_mgr ]() { 454SRV_INF ("%s: cleaning up before exit...\n" ,__func__ ); 455// stop the session GC first, it finalizes live sessions and wakes pending readers 456server_stream_session_manager_stop (); 457ctx_http .stop (); 458ctx_server .terminate (); 459mcp_mgr .shutdown (); 460llama_backend_free (); 461 }; 462 463// start the HTTP server before loading the model to be able to serve /health requests 464if (!ctx_http .start ()) { 465clean_up (); 466SRV_ERR ("%s" ,"exiting due to HTTP server error\n" ); 467return 1 ; 468 } 469 470// setup communication child --> router if necessary 471if (child .is_child ()) { 472ctx_server .set_state_callback ([& ](server_state state ,json payload ) { 473child .notify_to_router (server_state_to_str (state ),payload ); 474 }); 475 } 476 477if (!ctx_server .load_model (params )) { 478clean_up (); 479if (ctx_http .thread .joinable ()) { 480ctx_http .thread .join (); 481 } 482SRV_ERR ("%s" ,"exiting due to model loading error\n" ); 483return 1 ; 484 } 485 486routes .update_meta (ctx_server ); 487ctx_http .is_ready .store (true); 488 489SRV_INF ("%s" ,"model loaded\n" ); 490 491shutdown_handler = [& ](int ) { 492mcp_mgr .shutdown (); 493// this will unblock start_loop() 494ctx_server .terminate (); 495 }; 496 } 497 498// register signal handler if not running by CLI 499if (!is_run_by_cli ) { 500#if defined (__unix__ )|| (defined (__APPLE__ )&& defined (__MACH__ )) 501struct sigaction sigint_action ; 502sigint_action .sa_handler = signal_handler ; 503sigemptyset (& sigint_action .sa_mask ); 504sigint_action .sa_flags = 0 ; 505sigaction (SIGINT ,& sigint_action ,NULL ); 506sigaction (SIGTERM ,& sigint_action ,NULL ); 507#elif defined (_WIN32 ) 508auto console_ctrl_handler = + [](DWORD ctrl_type )-> BOOL { 509return (ctrl_type == CTRL_C_EVENT ) ? (signal_handler (SIGINT ), true) : false; 510 }; 511SetConsoleCtrlHandler (reinterpret_cast < PHANDLER_ROUTINE > (console_ctrl_handler ), true); 512#endif 513 } 514 515SRV_INF ("listening on %s\n" ,ctx_http .listening_address .c_str ()); 516 517// TODO: remove this in the future 518// check the string to also handle the .sock case 519if (string_ends_with (ctx_http .listening_address ,":8080" )) { 520SRV_WRN ("%s" ,"NOTICE: server default port will be changed to :9931 in a future release\n" ); 521SRV_WRN ("%s" ," ref: https://github.com/ggml-org/llama.cpp/pull/26508\n" ); 522 } 523 524if (is_router_server ) { 525if (!params .models_preset_hf .empty ()) { 526SRV_WRN ("NOTE: using preset.ini from HF repo '%s'\n" ,params .models_preset_hf .c_str ()); 527SRV_WRN ("%s" ," please only use presets that you can trust! Unknown presets may be unsafe\n" ); 528 } 529 530if (ctx_http .thread .joinable ()) { 531ctx_http .thread .join ();// keep the main thread alive 532 } 533 534// when the HTTP server stops, clean up and exit 535clean_up (); 536 }else { 537// optionally, notify router server that this instance is ready 538 std::thread monitor_thread ; 539if (child .is_child ()) { 540monitor_thread = child .setup (shutdown_handler ); 541child .notify_to_router (server_state_to_str (SERVER_STATE_READY ),routes .get_model_info ()); 542 } 543 544// this call blocks the main thread until queue_tasks.terminate() is called 545ctx_server .start_loop (); 546 547clean_up (); 548if (ctx_http .thread .joinable ()) { 549ctx_http .thread .join (); 550 } 551if (monitor_thread .joinable ()) { 552monitor_thread .join (); 553 } 554 555auto * ll_ctx = ctx_server .get_llama_context (); 556if (ll_ctx != nullptr ) { 557common_memory_breakdown_print (ll_ctx ); 558 } 559 } 560 561return 0 ; 562}