char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
82d6bb284
char/aliceai-t5-moe
1#pragma once 2 3#include "common.h" 4#include "download.h" 5#include "preset.h" 6#include "server-common.h" 7#include "server-http.h" 8#include "server-queue.h" 9 10#include <mutex> 11#include <condition_variable> 12#include <thread> 13#include <functional> 14#include <memory> 15#include <optional> 16#include <set> 17#include <string> 18#include <unordered_map> 19 20/** 21* state diagram: 22* 23* DOWNLOADING ──► DOWNLOADED ──► (replaced by new instance) 24* 25* UNLOADED ──► LOADING ──► LOADED ◄──── SLEEPING 26* ▲ │ │ ▲ 27* └───failed───┘ │ │ 28* ▲ └──sleeping─────┘ 29* └────────unloaded─────────┘ 30*/ 31enum server_model_status { 32// TODO: also add downloading state when the logic is added 33SERVER_MODEL_STATUS_DOWNLOADING , 34SERVER_MODEL_STATUS_DOWNLOADED , 35SERVER_MODEL_STATUS_UNLOADED , 36SERVER_MODEL_STATUS_LOADING , 37SERVER_MODEL_STATUS_LOADED , 38SERVER_MODEL_STATUS_SLEEPING 39}; 40 41enum server_model_source { 42SERVER_MODEL_SOURCE_PRESET , 43SERVER_MODEL_SOURCE_MODELS_DIR , 44SERVER_MODEL_SOURCE_CACHE , 45}; 46 47enum server_child_mode { 48SERVER_CHILD_MODE_NORMAL ,// load the model and run normally 49SERVER_CHILD_MODE_DOWNLOAD ,// download the model and exit 50}; 51 52static std ::string server_model_status_to_string (server_model_status status ) { 53switch (status ) { 54case SERVER_MODEL_STATUS_DOWNLOADING :return "downloading" ; 55case SERVER_MODEL_STATUS_DOWNLOADED :return "downloaded" ; 56case SERVER_MODEL_STATUS_UNLOADED :return "unloaded" ; 57case SERVER_MODEL_STATUS_LOADING :return "loading" ; 58case SERVER_MODEL_STATUS_LOADED :return "loaded" ; 59case SERVER_MODEL_STATUS_SLEEPING :return "sleeping" ; 60default :return "unknown" ; 61 } 62} 63 64static std ::string server_model_source_to_string (server_model_source source ) { 65switch (source ) { 66case SERVER_MODEL_SOURCE_PRESET :return "preset" ; 67case SERVER_MODEL_SOURCE_MODELS_DIR :return "models_dir" ; 68case SERVER_MODEL_SOURCE_CACHE :return "cache" ; 69default :return "unknown" ; 70 } 71} 72 73struct server_model_meta { 74server_model_source source = SERVER_MODEL_SOURCE_CACHE ; 75common_preset preset ; 76std ::string name ; 77std ::set < std ::string > aliases ;// additional names that resolve to this model 78std ::set < std ::string > tags ;// informational tags, not used for routing 79int port = 0 ; 80server_model_status status = SERVER_MODEL_STATUS_UNLOADED ; 81int64_t last_used = 0 ;// for LRU unloading 82std ::vector < std ::string > args ;// args passed to the model instance, will be populated by render_args() 83json loaded_info ;// info to be reflected via /v1/models endpoint ; if in DOWNLOADING state, it should contain download progress info 84json progress ;// reflect load or download progress info, if any 85int exit_code = 0 ;// exit code of the model instance process (only valid if status == FAILED) 86int stop_timeout = 0 ;// seconds to wait before force-killing the model instance during shutdown 87mtmd_caps multimodal ;// multimodal capabilities 88bool hidden = false;// hidden from GET /models, but still accept if requested 89 90bool is_ready ()const { 91return status == SERVER_MODEL_STATUS_LOADED ; 92 } 93 94bool is_running ()const { 95return status == SERVER_MODEL_STATUS_LOADED || status == SERVER_MODEL_STATUS_LOADING || status == SERVER_MODEL_STATUS_SLEEPING ; 96 } 97 98bool is_ready_or_sleep ()const { 99return status == SERVER_MODEL_STATUS_LOADED || status == SERVER_MODEL_STATUS_SLEEPING ; 100 } 101 102bool is_failed ()const { 103return status == SERVER_MODEL_STATUS_UNLOADED && exit_code != 0 ; 104 } 105 106void update_args (common_preset_context & ctx_presets ,std ::string bin_path ); 107void update_caps (); 108}; 109 110struct server_models_routes ; 111struct server_lru_sched ;// defined in server-models.cpp 112struct server_monitor ;// defined in server-models.cpp 113 114struct server_models { 115friend struct server_models_routes ; 116friend struct server_lru_sched ; 117friend struct server_monitor ; 118 119private : 120struct instance_t { 121std ::shared_ptr < server_subproc > subproc ;// shared with the monitor thread 122server_model_meta meta ; 123int req_count = 0 ;// number of active proxy requests 124 125// ask the child to exit (it handles the command on its stdin, see server_child::setup) 126void request_exit ()const ; 127 }; 128 129std ::mutex mutex ; 130std ::condition_variable cv ; 131std ::map < std ::string ,instance_t > mapping ; 132 133// models asked to stop, still counted as running until the monitor records their exit 134std ::set < std ::string > stopping_models ; 135 136// set to true while load_models() is executing a reload; load() will wait until clear 137bool is_reloading = false; 138 139// if true, the next get_meta() will trigger a reload of model list 140bool need_reload = false; 141 142// models marked with load-on-startup, unset once load_startup_models() drains it 143// no value means the startup phase is over, so a reload must not queue anything 144std ::optional < std ::vector < std ::string >>startup_models {std ::in_place }; 145 146// conv_id -> model name that currently serves its stream session, lets the resumable stream 147// routes go straight to the owning child instead of polling every one. populated when 148// proxy_request forwards a POST carrying an X-Conversation-Id. best effort: a stale entry just 149// makes the child answer not found and the client recovers. owns its lock, one mutex per struct 150struct conv_model_tracker { 151// returns the ticket of this registration, 0 when nothing was registered. erasing or 152// replacing the entry invalidates the ticket, which is how a stop cancels a request 153// parked in the model load wait 154uint64_t remember (const std ::string & conv_id ,const std ::string & model ) { 155if (conv_id .empty ()|| model .empty ()) { 156return 0 ; 157 } 158std ::lock_guard < std ::mutex > lock (mu ); 159uint64_t ticket = next_ticket ++ ; 160map [ conv_id ] = { model , ticket }; 161return ticket; 162} 163 164// false means a stop erased the entry or a newer request replaced it 165bool alive ( const std :: string & conv_id , uint64_t ticket ) { 166std :: lock_guard < std :: mutex > lock ( mu ); 167auto it = map. find (conv_id); 168return it != map. end () && it -> second . ticket == ticket; 169} 170 171std ::optional < std::string > lookup (const std::string & conv_id) { 172if (conv_id. empty ()) { 173return std::nullopt; 174} 175std ::lock_guard < std::mutex > lock (mu); 176auto it = map. find (conv_id); 177if (it == map. end ()) { 178return std::nullopt; 179} 180return it -> second . model ; 181} 182 183void forget ( const std ::string & conv_id) { 184if (conv_id. empty ()) { 185return ; 186} 187std ::lock_guard < std::mutex > lock (mu); 188map. erase (conv_id); 189} 190 191private : 192struct entry_t { 193std :: string model ; 194uint64_t ticket ; 195}; 196std :: mutex mu; 197uint64_t next_ticket = 1 ; 198std ::unordered_map < std::string, entry_t > map; 199}; 200 201common_preset_context ctx_preset; 202 203common_params base_params; 204std :: string bin_path; 205std ::vector < std::string > base_env; 206common_preset base_preset; // base preset from llama-server CLI args 207 208// queue of requests waiting for a models_max slot 209std ::unique_ptr < server_lru_sched > sched; 210 211// if true, add some delay to simulate works (useful for testing) 212bool debug_fake_timing = false; 213 214void update_meta ( const std ::string & name, const server_model_meta & meta); 215 216// unload least recently used models if the limit is reached 217void unload_lru (); 218 219// not thread-safe, caller must hold mutex 220void add_model ( server_model_meta && meta); 221 222// ask the monitor to stop a running instance; send_exit is false for a child that was already force-killed 223// not thread-safe, caller must hold mutex 224void request_stop ( const std ::string & name, bool send_exit = true); 225 226// called by the monitor once a child exited and was reaped 227void on_child_exit ( const std ::string & name, const std ::shared_ptr < server_subproc > & proc, server_child_mode mode, int exit_code); 228 229// notify SSE clients 230void notify_sse ( const std ::string & event, const std ::string & model_id, const json & data = nullptr ); 231 232public : 233// conv_id -> model tracker for the resumable stream routes, owns its lock 234conv_model_tracker conv_models; 235 236server_models( const common_params & params, int argc, char ** argv); 237~ server_models (); 238 239server_response sse; // for real-time updates via SSE endpoint 240 241// (re-)load the list of models from various sources and prepare the metadata mapping 242// - if this is called the first time, simply populate the metadata 243// - if this is called subsequently (e.g. when refreshing from disk): 244// - if a model is running but updated or removed from the source, it will be unloaded 245// - if a model is not running, it will be added or updated according to the source 246void load_models (); 247 248// lazy-load startup_models, to be called after main() setup phase 249void load_startup_models (); 250 251// check if a model instance exists (thread-safe) 252bool has_model ( const std ::string & name); 253 254// return a copy of model metadata (thread-safe) 255std ::optional < server_model_meta > get_meta (const std::string & name); 256 257// return a copy of all model metadata (thread-safe) 258std ::vector < server_model_meta > get_all_meta (); 259 260struct load_options { 261server_child_mode mode = SERVER_CHILD_MODE_NORMAL ; 262// used for spawning a downloading child process 263std :: optional < server_model_meta > custom_meta = std ::nullopt; 264}; 265 266// load and unload model instances 267// these functions are thread-safe 268void load ( const std ::string & name); 269void load ( const std ::string & name, const load_options & opts); 270void unload ( const std ::string & name); 271void unload_all (); 272 273struct update_status_args { 274server_model_status status ; 275int exit_code = 0 ; // only valid if status == UNLOADED 276json loaded_info = nullptr; 277json progress = nullptr; 278}; 279// update the status of a model instance (thread-safe) 280// also send SSE notification to /models/sse endpoint 281void update_status ( const std ::string & name, const update_status_args & args); 282void update_download_progress ( const std ::string & name, const common_download_progress & progress, bool done, bool ok = true); 283 284// remove a cache model from disk and update the list (thread-safe) 285// note: only cache models can be removed; returns false if the model doesn't exist or is not a cache model 286bool remove ( const std ::string & name); 287 288// wait until the model instance is fully loaded (thread-safe) 289// note: predicate is called while holding the lock 290// return when the model no longer in "loading" state 291void wait ( const std ::string & name, std ::function < bool ( const server_model_meta & ) > predicate); 292void wait ( std ::unique_lock < std::mutex > & lk, const std ::string & name, std ::function < bool ( const server_model_meta & ) > predicate); 293 294// ensure the model is in ready state (thread-safe) 295// return false if model is ready 296// otherwise, load the model and blocking wait until it's ready, then return true (meta may need to be refreshed) 297// if models_max is reached, the request waits in a queue until a slot frees up 298// throws if the load fails, or if should_stop fires while waiting 299bool ensure_model_ready ( const std ::string & name, const std ::function < bool () > & should_stop = nullptr ); 300 301// proxy an HTTP request to the model instance 302server_http_res_ptr proxy_request ( const server_http_req & req, const std ::string & method, const std ::string & name, bool update_last_used, bool detached = false); 303 304// handle message sent from server_child::notify_to_router() 305// raw input must starts with CMD_CHILD_TO_ROUTER_STATE, followed by a JSON string 306// called from the monitor thread 307// payload per state: 308// state = loading -> payload = {} (TODO: add progress info) 309// state = ready -> payload = model_info (json), or {} if wakeup from sleeping 310// state = sleeping -> payload = {} 311void handle_child_state ( const std ::string & name, const std ::string & raw_input); 312 313private : 314// one thread watching every child; keep last, the destructor joins the thread 315std ::unique_ptr < server_monitor > monitor; 316}; 317 318struct server_child { 319// serializes the notify_to_router writes 320std :: mutex mtx_stdout ; 321std :: atomic < bool > is_finished_downloading = false; // set by run_download 322 323// return true if the current process is a child server instance 324bool is_child (); 325server_child_mode get_mode (); 326int run_download ( common_params & params); 327 328// register the shutdown_handler to be called by the router 329// return the monitoring thread (to be joined by the caller) 330std :: thread setup ( const std ::function < void (int) > & shutdown_handler); 331 332// notify router server for status changes (e.g. loading, downloading, sleeping, etc.) 333// message will be handled by server_models::handle_child_state() on the router side 334void notify_to_router ( const std ::string & state_name, const json & payload ); 335}; 336 337struct server_models_routes { 338common_params params ; 339json ui_settings = json:: object (); // Primary: new name 340std :: atomic < bool > stopping = false; // for graceful disconnecting SSE clients during shutdown 341server_models models ; 342server_models_routes( const common_params & params, int argc, char ** argv) 343: params ( params ), models ( params , argc , argv ) { 344const std::string & cfg = this -> params . ui_config_json ; 345if (! cfg .empty()) { 346try { 347json json_settings = json :: parse (cfg); 348ui_settings = json_settings ; 349} catch ( const std ::exception & e) { 350LOG_ERR ( "%s: failed to parse UI config: %s\n" , __func__, e. what ()); 351throw; 352} 353} 354init_routes (); 355} 356 357void init_routes (); 358// handlers using lambda function, so that they can capture `this` without `std::bind` 359server_http_context :: handler_t get_router_props; 360server_http_context :: handler_t proxy_get; 361server_http_context :: handler_t proxy_post; 362server_http_context :: handler_t get_router_models; 363server_http_context :: handler_t post_router_models_load; 364server_http_context :: handler_t post_router_models_unload; 365// management API 366server_http_context :: handler_t get_router_models_sse; 367server_http_context :: handler_t post_router_models; 368server_http_context :: handler_t del_router_models; 369 370// router side handlers for the resumable streaming routes. each resolves the child that owns 371// a conversation through the conv_id -> model map, no probing or fan out 372server_http_context :: handler_t router_stream_get; 373server_http_context :: handler_t router_streams_lookup; 374server_http_context :: handler_t router_stream_delete; 375}; 376 377/** 378* A simple HTTP proxy that forwards requests to another server 379* and relays the responses back. 380*/ 381struct server_http_proxy : server_http_res { 382std ::function < void () > cleanup = nullptr ; 383server_http_proxy ( const std ::string & method, 384const std ::string & scheme, 385const std ::string & host, 386int port, 387const std ::string & path, 388const std ::map < std::string, std::string > & headers, 389const std ::string & body, 390const std ::map < std::string, uploaded_file > & files, 391const std ::function < bool () > should_stop, 392int32_t timeout_read, 393int32_t timeout_write 394); 395~ server_http_proxy () { 396if (cleanup_pipes) { 397cleanup_pipes (); 398} 399if (cleanup) { 400cleanup (); 401} 402} 403private : 404std ::function < void () > cleanup_pipes = nullptr ; 405std :: thread thread; 406struct msg_t { 407std :: map < std ::string, std ::string > headers; 408int status = 0 ; 409std :: string data ; 410std :: string content_type ; 411 }; 412};