2024-03-07 11:41:53 +02:00
#include "utils.hpp"
2024-09-09 23:36:09 +02:00
#include "arg.h"
2023-05-21 11:51:18 -06:00
#include "common.h"
2024-09-15 20:46:12 +03:00
#include "log.h"
2024-09-09 23:36:09 +02:00
#include "sampling.h"
2024-03-21 11:50:43 +00:00
#include "json-schema-to-grammar.h"
2023-05-21 11:51:18 -06:00
#include "llama.h"
2024-05-08 21:53:08 +02:00
// Change JSON_ASSERT from assert() to GGML_ASSERT:
#define JSON_ASSERT GGML_ASSERT
2023-06-17 07:53:04 -04:00
#include "json.hpp"
2024-08-16 17:19:05 +02:00
// mime type for sending response
#define MIMETYPE_JSON "application/json; charset=utf-8"
2023-05-21 11:51:18 -06:00
2023-07-04 10:05:27 -04:00
// auto generated files (update with ./deps.sh)
2024-06-01 21:31:48 +02:00
#include "colorthemes.css.hpp"
#include "style.css.hpp"
#include "theme-beeninorder.css.hpp"
#include "theme-ketivah.css.hpp"
#include "theme-mangotango.css.hpp"
#include "theme-playground.css.hpp"
#include "theme-polarnight.css.hpp"
#include "theme-snowstorm.css.hpp"
2023-07-04 10:05:27 -04:00
#include "index.html.hpp"
2024-06-01 21:31:48 +02:00
#include "index-new.html.hpp"
2023-07-04 10:05:27 -04:00
#include "index.js.hpp"
#include "completion.js.hpp"
2024-06-01 21:31:48 +02:00
#include "system-prompts.js.hpp"
#include "prompt-formats.js.hpp"
2023-08-15 06:14:14 +08:00
#include "json-schema-to-grammar.mjs.hpp"
2024-09-13 14:23:11 +02:00
#include "loading.html.hpp"
2023-07-04 10:05:27 -04:00
2024-03-07 11:41:53 +02:00
#include <atomic>
2023-12-29 06:24:12 -08:00
#include <condition_variable>
2024-03-07 11:41:53 +02:00
#include <cstddef>
2024-09-15 20:46:12 +03:00
#include <cinttypes>
2024-09-02 17:11:51 +02:00
#include <deque>
2024-09-15 20:46:12 +03:00
#include <memory>
#include <mutex>
#include <signal.h>
#include <thread>
#include <unordered_map>
#include <unordered_set>
2024-03-22 13:07:44 +00:00
using json = nlohmann :: ordered_json ;
2023-05-21 11:51:18 -06:00
2024-02-29 21:42:11 +01:00
enum stop_type {
2024-03-07 11:41:53 +02:00
STOP_TYPE_FULL ,
STOP_TYPE_PARTIAL ,
2023-06-17 07:53:04 -04:00
};
2023-05-21 11:51:18 -06:00
2024-09-06 23:21:29 +02:00
// state diagram: https://github.com/ggerganov/llama.cpp/pull/9283
2024-02-29 21:42:11 +01:00
enum slot_state {
2024-03-07 11:41:53 +02:00
SLOT_STATE_IDLE ,
2024-10-24 21:51:22 +02:00
SLOT_STATE_STARTED , // TODO: this state is only used for setting up the initial prompt processing; maybe merge it with launch_slot_with_task in the future
2024-09-06 23:21:29 +02:00
SLOT_STATE_PROCESSING_PROMPT ,
SLOT_STATE_DONE_PROMPT ,
SLOT_STATE_GENERATING ,
2024-03-07 11:41:53 +02:00
};
enum server_state {
SERVER_STATE_LOADING_MODEL , // Server is starting up, model not fully loaded yet
SERVER_STATE_READY , // Server is ready and model is loaded
};
enum server_task_type {
2024-10-24 21:51:22 +02:00
SERVER_TASK_TYPE_INFERENCE ,
2024-03-07 11:41:53 +02:00
SERVER_TASK_TYPE_CANCEL ,
SERVER_TASK_TYPE_NEXT_RESPONSE ,
2024-04-08 20:43:30 +08:00
SERVER_TASK_TYPE_METRICS ,
SERVER_TASK_TYPE_SLOT_SAVE ,
SERVER_TASK_TYPE_SLOT_RESTORE ,
SERVER_TASK_TYPE_SLOT_ERASE ,
2024-08-06 17:33:39 +02:00
SERVER_TASK_TYPE_SET_LORA ,
2024-03-07 11:41:53 +02:00
};
2024-10-24 21:51:22 +02:00
enum server_task_inf_type {
SERVER_TASK_INF_TYPE_COMPLETION ,
SERVER_TASK_INF_TYPE_EMBEDDING ,
SERVER_TASK_INF_TYPE_RERANK ,
SERVER_TASK_INF_TYPE_INFILL ,
2024-09-02 17:11:51 +02:00
};
2024-03-07 11:41:53 +02:00
struct server_task {
int id = - 1 ; // to be filled by server_queue
2024-09-02 17:11:51 +02:00
int id_target = - 1 ; // used by SERVER_TASK_TYPE_CANCEL
2024-03-07 11:41:53 +02:00
2024-10-24 21:51:22 +02:00
llama_tokens prompt_tokens ;
2024-03-07 11:41:53 +02:00
server_task_type type ;
json data ;
2024-10-24 21:51:22 +02:00
server_task_inf_type inf_type = SERVER_TASK_INF_TYPE_COMPLETION ;
2024-09-02 17:11:51 +02:00
// utility function
static std :: unordered_set < int > get_list_id ( const std :: vector < server_task > & tasks ) {
std :: unordered_set < int > ids ( tasks . size ());
for ( size_t i = 0 ; i < tasks . size (); i ++ ) {
ids . insert ( tasks [ i ]. id );
}
return ids ;
}
2024-03-07 11:41:53 +02:00
};
struct server_task_result {
int id = - 1 ;
json data ;
bool stop ;
bool error ;
};
2024-02-29 21:42:11 +01:00
struct slot_params {
bool stream = true ;
bool cache_prompt = false ; // remember the prompt to avoid reprocessing all prompt
2023-06-17 07:53:04 -04:00
2024-10-12 16:14:27 +03:00
int32_t n_keep = 0 ; // number of tokens to keep from initial prompt
int32_t n_discard = 0 ; // number of tokens after n_keep that may be discarded when shifting context, 0 defaults to half
int32_t n_predict = - 1 ; // new tokens to predict
2024-10-18 07:32:19 +03:00
int32_t n_indent = 0 ; // mininum line indentation for the generated text in number of whitespace characters
2024-10-12 16:14:27 +03:00
int64_t t_max_prompt_ms = - 1 ; // TODO: implement
int64_t t_max_predict_ms = - 1 ; // if positive, limit the generation phase to this time limit
2023-07-03 05:38:44 +08:00
2024-02-29 21:42:11 +01:00
std :: vector < std :: string > antiprompt ;
};
struct server_slot {
2023-10-22 22:53:08 +03:00
int id ;
2024-03-07 11:41:53 +02:00
int id_task = - 1 ;
2024-09-02 17:11:51 +02:00
// the index relative to completion multi-task request
size_t index = 0 ;
2023-10-22 22:53:08 +03:00
struct slot_params params ;
2024-03-07 11:41:53 +02:00
slot_state state = SLOT_STATE_IDLE ;
2023-10-22 22:53:08 +03:00
// used to determine the slot that has been used the longest
int64_t t_last_used = - 1 ;
// generation props
int32_t n_ctx = 0 ; // context size per slot
int32_t n_past = 0 ;
int32_t n_decoded = 0 ;
int32_t n_remaining = - 1 ;
int32_t i_batch = - 1 ;
2024-03-13 18:54:21 +01:00
int32_t n_predict = - 1 ; // TODO: disambiguate from params.n_predict
2023-10-22 22:53:08 +03:00
2024-10-24 21:51:22 +02:00
// n_prompt_tokens may not be equal to prompt_tokens.size(), because prompt maybe truncated
2024-02-29 21:42:11 +01:00
int32_t n_prompt_tokens = 0 ;
int32_t n_prompt_tokens_processed = 0 ;
2023-06-17 07:53:04 -04:00
2024-10-24 21:51:22 +02:00
// input prompt tokens
llama_tokens prompt_tokens ;
2024-03-07 11:41:53 +02:00
2024-10-18 07:32:19 +03:00
size_t last_nl_pos = 0 ;
2023-10-22 22:53:08 +03:00
std :: string generated_text ;
2024-10-24 21:51:22 +02:00
llama_tokens cache_tokens ;
2023-10-22 22:53:08 +03:00
std :: vector < completion_token_output > generated_token_probs ;
2023-06-17 07:53:04 -04:00
2024-10-24 21:51:22 +02:00
server_task_inf_type inf_type = SERVER_TASK_INF_TYPE_COMPLETION ;
2024-09-28 17:42:03 +03:00
2023-10-22 22:53:08 +03:00
bool has_next_token = true ;
2024-10-12 16:14:27 +03:00
bool has_new_line = false ;
2024-03-07 11:41:53 +02:00
bool truncated = false ;
bool stopped_eos = false ;
bool stopped_word = false ;
bool stopped_limit = false ;
2023-10-22 22:53:08 +03:00
2023-11-25 11:29:06 +02:00
bool oaicompat = false ;
2024-03-07 11:41:53 +02:00
std :: string oaicompat_model ;
2023-06-17 07:53:04 -04:00
std :: string stopping_word ;
2023-10-22 22:53:08 +03:00
// sampling
2024-03-21 11:50:43 +00:00
json json_schema ;
2023-07-04 10:05:27 -04:00
2024-10-10 22:57:42 +02:00
struct common_sampler_params sparams ;
struct common_sampler * smpl = nullptr ;
2024-09-07 15:16:19 +03:00
llama_token sampled ;
2023-10-22 22:53:08 +03:00
// stats
2024-10-12 16:06:31 +03:00
size_t n_sent_text = 0 ; // number of sent text character
2024-02-29 21:42:11 +01:00
size_t n_sent_token_probs = 0 ;
2023-10-22 22:53:08 +03:00
int64_t t_start_process_prompt ;
2024-03-07 11:41:53 +02:00
int64_t t_start_generation ;
2023-10-22 22:53:08 +03:00
double t_prompt_processing ; // ms
2024-10-12 16:06:31 +03:00
double t_token_generation ; // ms
2023-10-22 22:53:08 +03:00
2024-09-06 23:21:29 +02:00
std :: function < void ( int ) > callback_on_release ;
2023-10-22 22:53:08 +03:00
void reset () {
2024-09-15 20:46:12 +03:00
SLT_DBG ( * this , "%s" , " \n " );
2024-03-07 11:41:53 +02:00
n_prompt_tokens = 0 ;
2024-10-18 07:32:19 +03:00
last_nl_pos = 0 ;
2024-03-07 11:41:53 +02:00
generated_text = "" ;
2024-10-12 16:14:27 +03:00
has_new_line = false ;
2024-03-07 11:41:53 +02:00
truncated = false ;
stopped_eos = false ;
stopped_word = false ;
stopped_limit = false ;
stopping_word = "" ;
n_past = 0 ;
n_sent_text = 0 ;
n_sent_token_probs = 0 ;
2024-10-24 21:51:22 +02:00
inf_type = SERVER_TASK_INF_TYPE_COMPLETION ;
2024-01-30 20:17:30 +02:00
2023-10-22 22:53:08 +03:00
generated_token_probs . clear ();
2023-07-04 10:05:27 -04:00
}
2024-10-10 22:57:42 +02:00
bool has_budget ( common_params & global_params ) {
2024-02-29 21:42:11 +01:00
if ( params . n_predict == - 1 && global_params . n_predict == - 1 ) {
2024-01-07 08:45:26 +02:00
return true ; // limitless
}
2023-10-22 22:53:08 +03:00
n_remaining = - 1 ;
2024-01-07 08:45:26 +02:00
2024-02-29 21:42:11 +01:00
if ( params . n_predict != - 1 ) {
2023-10-22 22:53:08 +03:00
n_remaining = params . n_predict - n_decoded ;
2024-02-29 21:42:11 +01:00
} else if ( global_params . n_predict != - 1 ) {
2023-10-22 22:53:08 +03:00
n_remaining = global_params . n_predict - n_decoded ;
}
2024-01-07 08:45:26 +02:00
return n_remaining > 0 ; // no budget
2023-10-22 22:53:08 +03:00
}
bool is_processing () const {
2024-09-06 23:21:29 +02:00
return state != SLOT_STATE_IDLE ;
2023-10-22 22:53:08 +03:00
}
2024-09-15 20:46:12 +03:00
void add_token ( const completion_token_output & token ) {
2024-09-06 23:21:29 +02:00
if ( ! is_processing ()) {
2024-09-15 20:46:12 +03:00
SLT_WRN ( * this , "%s" , "slot is not processing \n " );
2023-10-22 22:53:08 +03:00
return ;
}
generated_token_probs . push_back ( token );
}
void release () {
2024-09-06 23:21:29 +02:00
if ( is_processing ()) {
2024-09-15 20:46:12 +03:00
SLT_INF ( * this , "stop processing: n_past = %d, truncated = %d \n " , n_past , truncated );
2024-03-07 11:41:53 +02:00
t_token_generation = ( ggml_time_us () - t_start_generation ) / 1e3 ;
2024-09-06 23:21:29 +02:00
state = SLOT_STATE_IDLE ;
callback_on_release ( id );
2023-10-22 22:53:08 +03:00
}
}
2024-03-07 11:41:53 +02:00
json get_formated_timings () const {
return json {
2024-02-29 21:42:11 +01:00
{ "prompt_n" , n_prompt_tokens_processed },
2023-10-22 22:53:08 +03:00
{ "prompt_ms" , t_prompt_processing },
2024-02-29 21:42:11 +01:00
{ "prompt_per_token_ms" , t_prompt_processing / n_prompt_tokens_processed },
{ "prompt_per_second" , 1e3 / t_prompt_processing * n_prompt_tokens_processed },
2023-10-22 22:53:08 +03:00
{ "predicted_n" , n_decoded },
{ "predicted_ms" , t_token_generation },
{ "predicted_per_token_ms" , t_token_generation / n_decoded },
{ "predicted_per_second" , 1e3 / t_token_generation * n_decoded },
};
}
2024-03-07 11:41:53 +02:00
size_t find_stopping_strings ( const std :: string & text , const size_t last_token_size , const stop_type type ) {
size_t stop_pos = std :: string :: npos ;
for ( const std :: string & word : params . antiprompt ) {
size_t pos ;
if ( type == STOP_TYPE_FULL ) {
const size_t tmp = word . size () + last_token_size ;
const size_t from_pos = text . size () > tmp ? text . size () - tmp : 0 ;
pos = text . find ( word , from_pos );
} else {
pos = find_partial_stop_string ( word , text );
}
if ( pos != std :: string :: npos && ( stop_pos == std :: string :: npos || pos < stop_pos )) {
if ( type == STOP_TYPE_FULL ) {
stopped_word = true ;
stopping_word = word ;
has_next_token = false ;
}
stop_pos = pos ;
}
}
return stop_pos ;
}
2023-11-25 11:29:06 +02:00
void print_timings () const {
2024-09-15 20:46:12 +03:00
const double t_prompt = t_prompt_processing / n_prompt_tokens_processed ;
const double n_prompt_second = 1e3 / t_prompt_processing * n_prompt_tokens_processed ;
2024-03-07 11:41:53 +02:00
2024-09-15 20:46:12 +03:00
const double t_gen = t_token_generation / n_decoded ;
const double n_gen_second = 1e3 / t_token_generation * n_decoded ;
2024-03-07 11:41:53 +02:00
2024-09-15 20:46:12 +03:00
SLT_INF ( * this ,
" \n "
" \r prompt eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second) \n "
" \r eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second) \n "
" \r total time = %10.2f ms / %5d tokens \n " ,
t_prompt_processing , n_prompt_tokens_processed , t_prompt , n_prompt_second ,
t_token_generation , n_decoded , t_gen , n_gen_second ,
t_prompt_processing + t_token_generation , n_prompt_tokens_processed + n_decoded );
2023-10-22 22:53:08 +03:00
}
};
2024-02-29 21:42:11 +01:00
struct server_metrics {
2024-03-09 17:34:15 +02:00
int64_t t_start = 0 ;
2024-03-08 12:25:04 +01:00
2024-02-25 13:49:43 +01:00
uint64_t n_prompt_tokens_processed_total = 0 ;
2024-03-08 12:25:04 +01:00
uint64_t t_prompt_processing_total = 0 ;
2024-02-25 13:49:43 +01:00
uint64_t n_tokens_predicted_total = 0 ;
2024-03-08 12:25:04 +01:00
uint64_t t_tokens_generation_total = 0 ;
2024-02-25 13:49:43 +01:00
uint64_t n_prompt_tokens_processed = 0 ;
uint64_t t_prompt_processing = 0 ;
2024-03-07 11:41:53 +02:00
uint64_t n_tokens_predicted = 0 ;
uint64_t t_tokens_generation = 0 ;
2024-02-25 13:49:43 +01:00
2024-09-06 23:21:29 +02:00
uint64_t n_decode_total = 0 ;
uint64_t n_busy_slots_total = 0 ;
2024-03-09 17:34:15 +02:00
void init () {
t_start = ggml_time_us ();
}
void on_prompt_eval ( const server_slot & slot ) {
2024-02-29 21:42:11 +01:00
n_prompt_tokens_processed_total += slot . n_prompt_tokens_processed ;
n_prompt_tokens_processed += slot . n_prompt_tokens_processed ;
t_prompt_processing += slot . t_prompt_processing ;
2024-03-08 12:25:04 +01:00
t_prompt_processing_total += slot . t_prompt_processing ;
2024-02-25 13:49:43 +01:00
}
2024-03-09 17:34:15 +02:00
void on_prediction ( const server_slot & slot ) {
2024-03-08 12:25:04 +01:00
n_tokens_predicted_total += slot . n_decoded ;
n_tokens_predicted += slot . n_decoded ;
t_tokens_generation += slot . t_token_generation ;
t_tokens_generation_total += slot . t_token_generation ;
2024-02-25 13:49:43 +01:00
}
2024-09-06 23:21:29 +02:00
void on_decoded ( const std :: vector < server_slot > & slots ) {
n_decode_total ++ ;
for ( const auto & slot : slots ) {
if ( slot . is_processing ()) {
n_busy_slots_total ++ ;
}
}
}
2024-02-25 13:49:43 +01:00
void reset_bucket () {
n_prompt_tokens_processed = 0 ;
t_prompt_processing = 0 ;
n_tokens_predicted = 0 ;
t_tokens_generation = 0 ;
}
};
2024-03-07 11:41:53 +02:00
struct server_queue {
int id = 0 ;
bool running ;
2023-10-22 22:53:08 +03:00
2024-03-07 11:41:53 +02:00
// queues
2024-09-02 17:11:51 +02:00
std :: deque < server_task > queue_tasks ;
std :: deque < server_task > queue_tasks_deferred ;
2024-03-07 11:41:53 +02:00
std :: mutex mutex_tasks ;
std :: condition_variable condition_tasks ;
// callback functions
2024-09-02 17:11:51 +02:00
std :: function < void ( server_task & ) > callback_new_task ;
std :: function < void ( void ) > callback_update_slots ;
2024-03-07 11:41:53 +02:00
// Add a new task to the end of the queue
2024-09-02 17:11:51 +02:00
int post ( server_task task , bool front = false ) {
2024-03-07 11:41:53 +02:00
std :: unique_lock < std :: mutex > lock ( mutex_tasks );
if ( task . id == - 1 ) {
task . id = id ++ ;
}
2024-09-15 20:46:12 +03:00
QUE_DBG ( "new task, id = %d, front = %d \n " , task . id , front );
2024-09-02 17:11:51 +02:00
if ( front ) {
queue_tasks . push_front ( std :: move ( task ));
} else {
queue_tasks . push_back ( std :: move ( task ));
}
2024-03-07 11:41:53 +02:00
condition_tasks . notify_one ();
return task . id ;
}
2024-09-02 17:11:51 +02:00
// multi-task version of post()
int post ( std :: vector < server_task > & tasks , bool front = false ) {
2024-09-06 14:06:04 +02:00
std :: unique_lock < std :: mutex > lock ( mutex_tasks );
2024-09-02 17:11:51 +02:00
for ( auto & task : tasks ) {
if ( task . id == - 1 ) {
task . id = id ++ ;
}
2024-09-15 20:46:12 +03:00
QUE_DBG ( "new task, id = %d/%d, front = %d \n " , task . id , ( int ) tasks . size (), front );
2024-09-02 17:11:51 +02:00
if ( front ) {
queue_tasks . push_front ( std :: move ( task ));
} else {
queue_tasks . push_back ( std :: move ( task ));
}
}
condition_tasks . notify_one ();
return 0 ;
}
2024-03-07 11:41:53 +02:00
// Add a new task, but defer until one slot is available
void defer ( server_task task ) {
std :: unique_lock < std :: mutex > lock ( mutex_tasks );
2024-09-15 20:46:12 +03:00
QUE_DBG ( "defer task, id = %d \n " , task . id );
2024-03-07 11:41:53 +02:00
queue_tasks_deferred . push_back ( std :: move ( task ));
2024-09-06 23:21:29 +02:00
condition_tasks . notify_one ();
2024-03-07 11:41:53 +02:00
}
2024-09-02 17:11:51 +02:00
// Get the next id for creating a new task
2024-03-07 11:41:53 +02:00
int get_new_id () {
std :: unique_lock < std :: mutex > lock ( mutex_tasks );
int new_id = id ++ ;
return new_id ;
}
// Register function to process a new task
void on_new_task ( std :: function < void ( server_task & ) > callback ) {
callback_new_task = std :: move ( callback );
}
// Register the function to be called when all slots data is ready to be processed
2024-03-11 10:56:41 +01:00
void on_update_slots ( std :: function < void ( void ) > callback ) {
callback_update_slots = std :: move ( callback );
2024-03-07 11:41:53 +02:00
}
2024-09-06 23:21:29 +02:00
// Call when the state of one slot is changed, it will move one task from deferred to main queue
void pop_deferred_task () {
2024-03-07 11:41:53 +02:00
std :: unique_lock < std :: mutex > lock ( mutex_tasks );
2024-09-06 23:21:29 +02:00
if ( ! queue_tasks_deferred . empty ()) {
queue_tasks . emplace_back ( std :: move ( queue_tasks_deferred . front ()));
queue_tasks_deferred . pop_front ();
2024-03-07 11:41:53 +02:00
}
2024-09-06 23:21:29 +02:00
condition_tasks . notify_one ();
2024-03-07 11:41:53 +02:00
}
// end the start_loop routine
void terminate () {
std :: unique_lock < std :: mutex > lock ( mutex_tasks );
running = false ;
condition_tasks . notify_all ();
}
/**
* Main loop consists of these steps:
* - Wait until a new task arrives
* - Process the task (i.e. maybe copy data into slot)
* - Check if multitask is finished
2024-03-11 10:56:41 +01:00
* - Update all slots
2024-03-07 11:41:53 +02:00
*/
void start_loop () {
running = true ;
while ( true ) {
2024-09-15 20:46:12 +03:00
QUE_DBG ( "%s" , "processing new tasks \n " );
2024-03-07 11:41:53 +02:00
while ( true ) {
std :: unique_lock < std :: mutex > lock ( mutex_tasks );
if ( queue_tasks . empty ()) {
lock . unlock ();
break ;
}
server_task task = queue_tasks . front ();
2024-09-06 23:21:29 +02:00
queue_tasks . pop_front ();
2024-03-07 11:41:53 +02:00
lock . unlock ();
2024-09-15 20:46:12 +03:00
QUE_DBG ( "processing task, id = %d \n " , task . id );
2024-03-07 11:41:53 +02:00
callback_new_task ( task );
}
// all tasks in the current loop is processed, slots data is now ready
2024-09-15 20:46:12 +03:00
QUE_DBG ( "%s" , "update slots \n " );
2024-03-07 11:41:53 +02:00
2024-03-11 10:56:41 +01:00
callback_update_slots ();
2024-03-07 11:41:53 +02:00
2024-09-15 20:46:12 +03:00
QUE_DBG ( "%s" , "waiting for new tasks \n " );
2024-03-07 11:41:53 +02:00
{
std :: unique_lock < std :: mutex > lock ( mutex_tasks );
if ( queue_tasks . empty ()) {
if ( ! running ) {
2024-09-15 20:46:12 +03:00
QUE_DBG ( "%s" , "terminate \n " );
2024-03-07 11:41:53 +02:00
return ;
}
condition_tasks . wait ( lock , [ & ]{
return ( ! queue_tasks . empty () || ! running );
});
}
}
}
}
};
struct server_response {
// for keeping track of all tasks waiting for the result
2024-09-02 17:11:51 +02:00
std :: unordered_set < int > waiting_task_ids ;
2024-03-07 11:41:53 +02:00
// the main result queue
std :: vector < server_task_result > queue_results ;
std :: mutex mutex_results ;
std :: condition_variable condition_results ;
// add the id_task to the list of tasks waiting for response
void add_waiting_task_id ( int id_task ) {
2024-09-19 12:44:53 +03:00
SRV_DBG ( "add task %d to waiting list. current waiting = %d (before add) \n " , id_task , ( int ) waiting_task_ids . size ());
2024-03-07 11:41:53 +02:00
std :: unique_lock < std :: mutex > lock ( mutex_results );
waiting_task_ids . insert ( id_task );
}
2024-09-02 17:11:51 +02:00
void add_waiting_tasks ( const std :: vector < server_task > & tasks ) {
2024-09-19 12:44:53 +03:00
std :: unique_lock < std :: mutex > lock ( mutex_results );
for ( const auto & task : tasks ) {
SRV_DBG ( "add task %d to waiting list. current waiting = %d (before add) \n " , task . id , ( int ) waiting_task_ids . size ());
waiting_task_ids . insert ( task . id );
2024-09-02 17:11:51 +02:00
}
}
2024-03-07 11:41:53 +02:00
// when the request is finished, we can remove task associated with it
void remove_waiting_task_id ( int id_task ) {
2024-09-19 12:44:53 +03:00
SRV_DBG ( "remove task %d from waiting list. current waiting = %d (before remove) \n " , id_task , ( int ) waiting_task_ids . size ());
2024-03-07 11:41:53 +02:00
std :: unique_lock < std :: mutex > lock ( mutex_results );
waiting_task_ids . erase ( id_task );
}
2024-09-19 12:44:53 +03:00
void remove_waiting_task_ids ( const std :: unordered_set < int > & id_tasks ) {
std :: unique_lock < std :: mutex > lock ( mutex_results );
for ( const auto & id_task : id_tasks ) {
SRV_DBG ( "remove task %d from waiting list. current waiting = %d (before remove) \n " , id_task , ( int ) waiting_task_ids . size ());
waiting_task_ids . erase ( id_task );
}
}
2024-09-02 17:11:51 +02:00
// This function blocks the thread until there is a response for one of the id_tasks
server_task_result recv ( const std :: unordered_set < int > & id_tasks ) {
2024-03-07 11:41:53 +02:00
while ( true ) {
std :: unique_lock < std :: mutex > lock ( mutex_results );
condition_results . wait ( lock , [ & ]{
return ! queue_results . empty ();
});
for ( int i = 0 ; i < ( int ) queue_results . size (); i ++ ) {
2024-09-02 17:11:51 +02:00
if ( id_tasks . find ( queue_results [ i ]. id ) != id_tasks . end ()) {
2024-03-07 11:41:53 +02:00
server_task_result res = queue_results [ i ];
queue_results . erase ( queue_results . begin () + i );
return res ;
}
}
}
// should never reach here
}
2024-09-02 17:11:51 +02:00
// single-task version of recv()
server_task_result recv ( int id_task ) {
std :: unordered_set < int > id_tasks = { id_task };
return recv ( id_tasks );
2024-03-07 11:41:53 +02:00
}
// Send a new result to a waiting id_task
2024-09-02 17:11:51 +02:00
void send ( server_task_result & result ) {
2024-09-15 20:46:12 +03:00
SRV_DBG ( "sending result for task id = %d \n " , result . id );
2024-03-07 11:41:53 +02:00
std :: unique_lock < std :: mutex > lock ( mutex_results );
for ( const auto & id_task : waiting_task_ids ) {
if ( result . id == id_task ) {
2024-09-15 20:46:12 +03:00
SRV_DBG ( "task id = %d moved to result queue \n " , result . id );
2024-09-02 17:11:51 +02:00
queue_results . push_back ( std :: move ( result ));
2024-03-07 11:41:53 +02:00
condition_results . notify_all ();
return ;
}
}
}
};
struct server_context {
llama_model * model = nullptr ;
llama_context * ctx = nullptr ;
2024-10-10 22:57:42 +02:00
std :: vector < common_lora_adapter_container > loras ;
2023-10-22 22:53:08 +03:00
2024-10-10 22:57:42 +02:00
common_params params ;
2023-10-22 22:53:08 +03:00
2024-09-09 17:10:46 +02:00
llama_batch batch = {};
2023-10-22 22:53:08 +03:00
2024-03-07 11:41:53 +02:00
bool clean_kv_cache = true ;
bool add_bos_token = true ;
2024-08-12 10:21:50 +03:00
bool has_eos_token = false ;
2023-10-22 22:53:08 +03:00
2024-03-07 11:41:53 +02:00
int32_t n_ctx ; // total context for all clients / slots
2023-10-22 22:53:08 +03:00
// slots / clients
2024-02-29 21:42:11 +01:00
std :: vector < server_slot > slots ;
2024-02-05 08:10:22 +00:00
json default_generation_settings_for_props ;
2023-10-22 22:53:08 +03:00
2024-03-07 11:41:53 +02:00
server_queue queue_tasks ;
server_response queue_results ;
2023-10-22 22:53:08 +03:00
2024-02-29 21:42:11 +01:00
server_metrics metrics ;
2024-02-25 13:49:43 +01:00
2024-06-08 07:50:31 +00:00
// Necessary similarity of prompt for slot selection
float slot_prompt_similarity = 0.0f ;
2024-03-07 11:41:53 +02:00
~ server_context () {
if ( ctx ) {
2023-06-17 07:53:04 -04:00
llama_free ( ctx );
ctx = nullptr ;
}
2024-03-07 11:41:53 +02:00
if ( model ) {
2023-06-24 11:47:58 +03:00
llama_free_model ( model );
model = nullptr ;
}
2024-05-11 04:13:02 -04:00
2024-05-14 10:11:24 -04:00
// Clear any sampling context
for ( server_slot & slot : slots ) {
2024-09-07 15:16:19 +03:00
if ( slot . smpl != nullptr ) {
2024-10-10 22:57:42 +02:00
common_sampler_free ( slot . smpl );
2024-05-14 10:11:24 -04:00
}
}
2024-05-11 04:13:02 -04:00
llama_batch_free ( batch );
2023-06-17 07:53:04 -04:00
}
2024-10-10 22:57:42 +02:00
bool load_model ( const common_params & params_ ) {
2023-06-17 07:53:04 -04:00
params = params_ ;
2023-10-22 22:53:08 +03:00
2024-10-12 14:51:54 +03:00
// reserve one extra sequence (seq_id == 0) for extra features
2024-03-08 17:31:00 -05:00
params . n_parallel += 1 ;
2024-10-10 22:57:42 +02:00
common_init_result llama_init = common_init_from_params ( params );
2024-08-06 00:14:10 +08:00
model = llama_init . model ;
2024-09-15 20:46:12 +03:00
ctx = llama_init . context ;
loras = llama_init . lora_adapters ;
2024-03-08 17:31:00 -05:00
params . n_parallel -= 1 ; // but be sneaky about it
2024-09-15 20:46:12 +03:00
2024-03-07 11:41:53 +02:00
if ( model == nullptr ) {
2024-09-15 20:46:12 +03:00
SRV_ERR ( "failed to load model, '%s' \n " , params . model . c_str ());
2023-06-17 07:53:04 -04:00
return false ;
}
2023-10-22 22:53:08 +03:00
2023-09-28 21:42:38 +02:00
n_ctx = llama_n_ctx ( ctx );
2023-10-22 22:53:08 +03:00
2024-08-15 15:23:23 +08:00
add_bos_token = llama_add_bos_token ( model );
has_eos_token = ! llama_add_eos_token ( model );
2024-08-16 17:19:05 +02:00
2023-06-17 07:53:04 -04:00
return true ;
}
2024-03-07 11:41:53 +02:00
bool validate_model_chat_template () const {
2024-02-22 09:33:24 +01:00
llama_chat_message chat [] = {{ "user" , "test" }};
2024-03-07 11:41:53 +02:00
const int res = llama_chat_apply_template ( model , nullptr , chat , 1 , true , nullptr , 0 );
return res > 0 ;
2024-02-22 09:33:24 +01:00
}
2024-03-09 17:34:15 +02:00
void init () {
2023-10-22 22:53:08 +03:00
const int32_t n_ctx_slot = n_ctx / params . n_parallel ;
2024-09-15 20:46:12 +03:00
SRV_INF ( "initializing slots, n_slots = %d \n " , params . n_parallel );
2024-03-09 17:34:15 +02:00
2024-03-07 11:41:53 +02:00
for ( int i = 0 ; i < params . n_parallel ; i ++ ) {
2024-02-29 21:42:11 +01:00
server_slot slot ;
2023-10-22 22:53:08 +03:00
slot . id = i ;
slot . n_ctx = n_ctx_slot ;
2024-02-18 17:30:09 +01:00
slot . n_predict = params . n_predict ;
2023-10-22 22:53:08 +03:00
2024-09-15 20:46:12 +03:00
SLT_INF ( slot , "new slot n_ctx_slot = %d \n " , slot . n_ctx );
2024-01-27 14:38:05 +01:00
2024-07-10 20:08:17 -04:00
slot . sparams = params . sparams ;
2024-09-06 23:21:29 +02:00
slot . callback_on_release = [ this ]( int ) {
queue_tasks . pop_deferred_task ();
};
2024-01-27 14:38:05 +01:00
slot . reset ();
2023-10-22 22:53:08 +03:00
slots . push_back ( slot );
}
2024-02-05 08:10:22 +00:00
default_generation_settings_for_props = get_formated_generation ( slots . front ());
default_generation_settings_for_props [ "seed" ] = - 1 ;
2024-08-14 02:51:02 -04:00
// the update_slots() logic will always submit a maximum of n_batch or n_parallel tokens
2024-03-13 18:54:21 +01:00
// note that n_batch can be > n_ctx (e.g. for non-causal attention models such as BERT where the KV cache is not used)
{
const int32_t n_batch = llama_n_batch ( ctx );
2024-03-26 10:46:41 -04:00
// only a single seq_id per token is needed
2024-08-14 02:51:02 -04:00
batch = llama_batch_init ( std :: max ( n_batch , params . n_parallel ), 0 , 1 );
2024-03-13 18:54:21 +01:00
}
2024-03-09 17:34:15 +02:00
metrics . init ();
2023-10-22 22:53:08 +03:00
}
2024-06-08 07:50:31 +00:00
server_slot * get_slot_by_id ( int id ) {
2024-03-07 11:41:53 +02:00
for ( server_slot & slot : slots ) {
2024-06-08 07:50:31 +00:00
if ( slot . id == id ) {
2023-10-22 22:53:08 +03:00
return & slot ;
}
}
2023-10-20 21:07:23 +03:00
2024-06-08 07:50:31 +00:00
return nullptr ;
}
2024-11-01 13:33:14 +00:00
server_slot * get_available_slot ( const server_task & task ) {
2024-06-08 07:50:31 +00:00
server_slot * ret = nullptr ;
// find the slot that has at least n% prompt similarity
2024-11-01 13:33:14 +00:00
if ( ret == nullptr && slot_prompt_similarity != 0.0f ) {
int max_lcs_len = 0 ;
2024-06-08 07:50:31 +00:00
float similarity = 0 ;
for ( server_slot & slot : slots ) {
// skip the slot if it is not available
2024-09-06 23:21:29 +02:00
if ( slot . is_processing ()) {
2024-06-08 07:50:31 +00:00
continue ;
}
2024-10-24 21:51:22 +02:00
// skip the slot if it does not contains cached tokens
2024-11-01 13:33:14 +00:00
if ( slot . cache_tokens . empty ()) {
2024-06-12 14:42:29 +03:00
continue ;
}
2024-11-01 13:33:14 +00:00
// length of the Longest Common Subsequence between the current slot's prompt and the input prompt
int lcs_len = longest_common_subsequence ( slot . cache_tokens , task . prompt_tokens );
2024-06-08 07:50:31 +00:00
2024-11-01 13:33:14 +00:00
// fraction of the common subsequence length compared to the current slot's prompt length
similarity = static_cast < float > ( lcs_len ) / static_cast < int > ( slot . cache_tokens . size ());
2024-06-08 07:50:31 +00:00
// select the current slot if the criteria match
2024-11-01 13:33:14 +00:00
if ( lcs_len > max_lcs_len && similarity > slot_prompt_similarity ) {
max_lcs_len = lcs_len ;
2024-06-08 07:50:31 +00:00
ret = & slot ;
}
}
if ( ret != nullptr ) {
2024-11-01 13:33:14 +00:00
SLT_DBG ( * ret , "selected slot by lcs similarity, max_lcs_len = %d, similarity = %f \n " , max_lcs_len , similarity );
2024-06-08 07:50:31 +00:00
}
}
// find the slot that has been least recently used
if ( ret == nullptr ) {
int64_t t_last = ggml_time_us ();
for ( server_slot & slot : slots ) {
// skip the slot if it is not available
2024-09-06 23:21:29 +02:00
if ( slot . is_processing ()) {
2024-06-08 07:50:31 +00:00
continue ;
}
// select the current slot if the criteria match
if ( slot . t_last_used < t_last ) {
t_last = slot . t_last_used ;
ret = & slot ;
}
}
if ( ret != nullptr ) {
2024-09-15 20:46:12 +03:00
SLT_DBG ( * ret , "selected slot by lru, t_last = %" PRId64 " \n " , t_last );
2024-06-08 07:50:31 +00:00
}
}
return ret ;
2023-08-08 15:29:19 +02:00
}
2024-03-11 10:56:41 +01:00
bool launch_slot_with_task ( server_slot & slot , const server_task & task ) {
2023-10-22 22:53:08 +03:00
slot_params default_params ;
2024-07-09 18:26:40 -04:00
// Sampling parameter defaults are loaded from the global server context (but individual requests can still override them)
2024-09-07 15:16:19 +03:00
auto default_sparams = params . sparams ;
const auto & data = task . data ;
2023-10-10 09:31:21 +02:00
2023-11-25 11:29:06 +02:00
if ( data . count ( "__oaicompat" ) != 0 ) {
2024-03-07 11:41:53 +02:00
slot . oaicompat = true ;
slot . oaicompat_model = json_value ( data , "model" , std :: string ( DEFAULT_OAICOMPAT_MODEL ));
2023-11-25 11:29:06 +02:00
} else {
2024-03-07 11:41:53 +02:00
slot . oaicompat = false ;
slot . oaicompat_model = "" ;
2023-11-25 11:29:06 +02:00
}
2024-10-25 10:07:34 -06:00
slot . params . stream = json_value ( data , "stream" , false );
slot . params . cache_prompt = json_value ( data , "cache_prompt" , false );
slot . params . n_predict = json_value ( data , "n_predict" , json_value ( data , "max_tokens" , default_params . n_predict ));
slot . params . n_indent = json_value ( data , "n_indent" , default_params . n_indent );
slot . sparams . top_k = json_value ( data , "top_k" , default_sparams . top_k );
slot . sparams . top_p = json_value ( data , "top_p" , default_sparams . top_p );
slot . sparams . min_p = json_value ( data , "min_p" , default_sparams . min_p );
slot . sparams . xtc_probability = json_value ( data , "xtc_probability" , default_sparams . xtc_probability );
slot . sparams . xtc_threshold = json_value ( data , "xtc_threshold" , default_sparams . xtc_threshold );
slot . sparams . typ_p = json_value ( data , "typical_p" , default_sparams . typ_p );
slot . sparams . temp = json_value ( data , "temperature" , default_sparams . temp );
slot . sparams . dynatemp_range = json_value ( data , "dynatemp_range" , default_sparams . dynatemp_range );
slot . sparams . dynatemp_exponent = json_value ( data , "dynatemp_exponent" , default_sparams . dynatemp_exponent );
slot . sparams . penalty_last_n = json_value ( data , "repeat_last_n" , default_sparams . penalty_last_n );
slot . sparams . penalty_repeat = json_value ( data , "repeat_penalty" , default_sparams . penalty_repeat );
slot . sparams . penalty_freq = json_value ( data , "frequency_penalty" , default_sparams . penalty_freq );
slot . sparams . penalty_present = json_value ( data , "presence_penalty" , default_sparams . penalty_present );
slot . sparams . dry_multiplier = json_value ( data , "dry_multiplier" , default_sparams . dry_multiplier );
slot . sparams . dry_base = json_value ( data , "dry_base" , default_sparams . dry_base );
slot . sparams . dry_allowed_length = json_value ( data , "dry_allowed_length" , default_sparams . dry_allowed_length );
slot . sparams . dry_penalty_last_n = json_value ( data , "dry_penalty_last_n" , default_sparams . dry_penalty_last_n );
slot . sparams . mirostat = json_value ( data , "mirostat" , default_sparams . mirostat );
slot . sparams . mirostat_tau = json_value ( data , "mirostat_tau" , default_sparams . mirostat_tau );
slot . sparams . mirostat_eta = json_value ( data , "mirostat_eta" , default_sparams . mirostat_eta );
slot . sparams . penalize_nl = json_value ( data , "penalize_nl" , default_sparams . penalize_nl );
slot . params . n_keep = json_value ( data , "n_keep" , default_params . n_keep );
slot . params . n_discard = json_value ( data , "n_discard" , default_params . n_discard );
slot . sparams . seed = json_value ( data , "seed" , default_sparams . seed );
slot . sparams . n_probs = json_value ( data , "n_probs" , default_sparams . n_probs );
slot . sparams . min_keep = json_value ( data , "min_keep" , default_sparams . min_keep );
//slot.params.t_max_prompt_ms = json_value(data, "t_max_prompt_ms", default_params.t_max_prompt_ms); // TODO: implement
slot . params . t_max_predict_ms = json_value ( data , "t_max_predict_ms" , default_params . t_max_predict_ms );
if ( slot . sparams . dry_base < 1.0f )
{
slot . sparams . dry_base = default_sparams . dry_base ;
}
// sequence breakers for DRY
{
// Currently, this is not compatible with TextGen WebUI, Koboldcpp and SillyTavern format
// Ref: https://github.com/oobabooga/text-generation-webui/blob/d1af7a41ade7bd3c3a463bfa640725edb818ebaf/extensions/openai/typing.py#L39
if ( data . contains ( "dry_sequence_breakers" )) {
slot . sparams . dry_sequence_breakers = json_value ( data , "dry_sequence_breakers" , std :: vector < std :: string > ());
if ( slot . sparams . dry_sequence_breakers . empty ()) {
send_error ( task , "Error: dry_sequence_breakers must be a non-empty array of strings" , ERROR_TYPE_INVALID_REQUEST );
return false ;
}
}
}
2024-03-25 09:42:17 +01:00
// process "json_schema" and "grammar"
2024-05-08 21:53:08 +02:00
if ( data . contains ( "json_schema" ) && ! data . at ( "json_schema" ). is_null () && data . contains ( "grammar" ) && ! data . at ( "grammar" ). is_null ()) {
2024-03-25 09:42:17 +01:00
send_error ( task , "Either \" json_schema \" or \" grammar \" can be specified, but not both" , ERROR_TYPE_INVALID_REQUEST );
return false ;
2024-09-07 15:16:19 +03:00
}
if ( data . contains ( "json_schema" ) && ! data . contains ( "grammar" )) {
2024-03-21 11:50:43 +00:00
try {
2024-10-12 16:06:31 +03:00
auto schema = json_value ( data , "json_schema" , json :: object ());
slot . sparams . grammar = json_schema_to_grammar ( schema );
2024-03-21 11:50:43 +00:00
} catch ( const std :: exception & e ) {
send_error ( task , std :: string ( " \" json_schema \" : " ) + e . what (), ERROR_TYPE_INVALID_REQUEST );
return false ;
}
} else {
2024-10-12 16:06:31 +03:00
slot . sparams . grammar = json_value ( data , "grammar" , default_sparams . grammar );
2024-03-07 11:41:53 +02:00
}
if ( slot . n_predict > 0 && slot . params . n_predict > slot . n_predict ) {
2024-02-18 17:30:09 +01:00
// Might be better to reject the request with a 400 ?
2024-03-07 11:41:53 +02:00
slot . params . n_predict = slot . n_predict ;
2024-09-15 20:46:12 +03:00
SLT_WRN ( slot , "n_predict = %d exceeds server configuration, setting to %d" , slot . n_predict , slot . n_predict );
2024-02-18 17:30:09 +01:00
}
2023-10-22 22:53:08 +03:00
{
2024-03-07 11:41:53 +02:00
slot . sparams . logit_bias . clear ();
2023-10-22 22:53:08 +03:00
2024-08-12 10:21:50 +03:00
if ( json_value ( data , "ignore_eos" , false ) && has_eos_token ) {
2024-09-07 15:16:19 +03:00
slot . sparams . logit_bias . push_back ({ llama_token_eos ( model ), - INFINITY });
2024-03-07 11:41:53 +02:00
}
const auto & logit_bias = data . find ( "logit_bias" );
if ( logit_bias != data . end () && logit_bias -> is_array ()) {
const int n_vocab = llama_n_vocab ( model );
for ( const auto & el : * logit_bias ) {
2024-03-11 10:56:41 +01:00
// TODO: we may want to throw errors here, in case "el" is incorrect
2024-03-07 11:41:53 +02:00
if ( el . is_array () && el . size () == 2 ) {
float bias ;
if ( el [ 1 ]. is_number ()) {
bias = el [ 1 ]. get < float > ();
} else if ( el [ 1 ]. is_boolean () && ! el [ 1 ]. get < bool > ()) {
bias = - INFINITY ;
} else {
continue ;
}
if ( el [ 0 ]. is_number_integer ()) {
llama_token tok = el [ 0 ]. get < llama_token > ();
if ( tok >= 0 && tok < n_vocab ) {
2024-09-07 15:16:19 +03:00
slot . sparams . logit_bias . push_back ({ tok , bias });
2024-03-07 11:41:53 +02:00
}
} else if ( el [ 0 ]. is_string ()) {
2024-10-10 22:57:42 +02:00
auto toks = common_tokenize ( model , el [ 0 ]. get < std :: string > (), false );
2024-03-07 11:41:53 +02:00
for ( auto tok : toks ) {
2024-09-07 15:16:19 +03:00
slot . sparams . logit_bias . push_back ({ tok , bias });
2024-03-07 11:41:53 +02:00
}
}
}
}
}
}
{
slot . params . antiprompt . clear ();
const auto & stop = data . find ( "stop" );
if ( stop != data . end () && stop -> is_array ()) {
for ( const auto & word : * stop ) {
if ( ! word . empty ()) {
slot . params . antiprompt . push_back ( word );
}
}
}
}
{
2024-09-07 15:16:19 +03:00
const auto & samplers = data . find ( "samplers" );
if ( samplers != data . end () && samplers -> is_array ()) {
2024-03-07 11:41:53 +02:00
std :: vector < std :: string > sampler_names ;
2024-09-07 15:16:19 +03:00
for ( const auto & name : * samplers ) {
if ( name . is_string ()) {
sampler_names . emplace_back ( name );
2024-03-07 11:41:53 +02:00
}
}
2024-10-10 22:57:42 +02:00
slot . sparams . samplers = common_sampler_types_from_names ( sampler_names , false );
2024-03-07 11:41:53 +02:00
} else {
2024-09-07 15:16:19 +03:00
slot . sparams . samplers = default_sparams . samplers ;
2024-03-07 11:41:53 +02:00
}
}
{
2024-09-07 15:16:19 +03:00
if ( slot . smpl != nullptr ) {
2024-10-10 22:57:42 +02:00
common_sampler_free ( slot . smpl );
2024-03-07 11:41:53 +02:00
}
2024-09-07 15:16:19 +03:00
2024-10-10 22:57:42 +02:00
slot . smpl = common_sampler_init ( model , slot . sparams );
2024-09-07 15:16:19 +03:00
if ( slot . smpl == nullptr ) {
2024-03-11 10:56:41 +01:00
// for now, the only error that may happen here is invalid grammar
send_error ( task , "Failed to parse grammar" , ERROR_TYPE_INVALID_REQUEST );
return false ;
}
2024-03-07 11:41:53 +02:00
}
2024-10-24 21:51:22 +02:00
slot . state = SLOT_STATE_STARTED ;
2023-10-22 22:53:08 +03:00
2024-09-15 20:46:12 +03:00
SLT_INF ( slot , "%s" , "processing task \n " );
2023-10-22 22:53:08 +03:00
return true ;
2023-06-17 07:53:04 -04:00
}
2023-10-22 22:53:08 +03:00
void kv_cache_clear () {
2024-09-15 20:46:12 +03:00
SRV_DBG ( "%s" , "clearing KV cache \n " );
2024-03-07 11:41:53 +02:00
2023-10-22 22:53:08 +03:00
// clear the entire KV cache
2023-10-29 11:31:40 -06:00
llama_kv_cache_clear ( ctx );
2023-10-22 22:53:08 +03:00
clean_kv_cache = false ;
2023-06-17 07:53:04 -04:00
}
2024-03-07 11:41:53 +02:00
bool process_token ( completion_token_output & result , server_slot & slot ) {
2023-10-22 22:53:08 +03:00
// remember which tokens were sampled - used for repetition penalties during sampling
2024-10-10 22:57:42 +02:00
const std :: string token_str = common_token_to_piece ( ctx , result . tok , params . special );
2023-10-22 22:53:08 +03:00
slot . sampled = result . tok ;
2023-05-21 11:51:18 -06:00
2023-10-22 22:53:08 +03:00
// search stop word and delete it
slot . generated_text += token_str ;
slot . has_next_token = true ;
2023-05-21 11:51:18 -06:00
2023-12-13 23:57:15 +04:00
// check if there is incomplete UTF-8 character at the end
bool incomplete = false ;
2024-03-07 11:41:53 +02:00
for ( unsigned i = 1 ; i < 5 && i <= slot . generated_text . size (); ++ i ) {
2023-12-13 23:57:15 +04:00
unsigned char c = slot . generated_text [ slot . generated_text . size () - i ];
2024-03-07 11:41:53 +02:00
if (( c & 0xC0 ) == 0x80 ) {
2023-12-13 23:57:15 +04:00
// continuation byte: 10xxxxxx
continue ;
}
2024-03-07 11:41:53 +02:00
if (( c & 0xE0 ) == 0xC0 ) {
2023-12-13 23:57:15 +04:00
// 2-byte character: 110xxxxx ...
incomplete = i < 2 ;
2024-03-07 11:41:53 +02:00
} else if (( c & 0xF0 ) == 0xE0 ) {
2023-12-13 23:57:15 +04:00
// 3-byte character: 1110xxxx ...
incomplete = i < 3 ;
2024-03-07 11:41:53 +02:00
} else if (( c & 0xF8 ) == 0xF0 ) {
2023-12-13 23:57:15 +04:00
// 4-byte character: 11110xxx ...
incomplete = i < 4 ;
2023-06-17 07:53:04 -04:00
}
2023-12-13 23:57:15 +04:00
// else 1-byte character or invalid byte
break ;
2023-06-17 07:53:04 -04:00
}
2024-03-07 11:41:53 +02:00
if ( ! incomplete ) {
2024-02-29 21:42:11 +01:00
size_t pos = std :: min ( slot . n_sent_text , slot . generated_text . size ());
2024-03-07 11:41:53 +02:00
2023-10-22 22:53:08 +03:00
const std :: string str_test = slot . generated_text . substr ( pos );
2024-10-16 08:35:53 +00:00
bool send_text = true ;
2024-03-07 11:41:53 +02:00
size_t stop_pos = slot . find_stopping_strings ( str_test , token_str . size (), STOP_TYPE_FULL );
if ( stop_pos != std :: string :: npos ) {
2023-10-22 22:53:08 +03:00
slot . generated_text . erase (
slot . generated_text . begin () + pos + stop_pos ,
slot . generated_text . end ());
2024-02-29 21:42:11 +01:00
pos = std :: min ( slot . n_sent_text , slot . generated_text . size ());
2024-10-16 08:35:53 +00:00
} else if ( slot . has_next_token ) {
2024-03-07 11:41:53 +02:00
stop_pos = slot . find_stopping_strings ( str_test , token_str . size (), STOP_TYPE_PARTIAL );
2024-10-16 08:35:53 +00:00
send_text = stop_pos == std :: string :: npos ;
2023-10-22 22:53:08 +03:00
}
// check if there is any token to predict
2024-10-16 08:35:53 +00:00
if ( send_text ) {
2023-10-22 22:53:08 +03:00
// no send the stop word in the response
result . text_to_send = slot . generated_text . substr ( pos , std :: string :: npos );
2024-02-29 21:42:11 +01:00
slot . n_sent_text += result . text_to_send . size ();
2023-10-22 22:53:08 +03:00
// add the token to slot queue and cache
}
2024-03-07 11:41:53 +02:00
2024-09-15 20:46:12 +03:00
slot . add_token ( result );
2024-03-07 11:41:53 +02:00
if ( slot . params . stream ) {
2023-10-22 22:53:08 +03:00
send_partial_response ( slot , result );
}
2023-06-17 07:53:04 -04:00
}
2024-03-07 11:41:53 +02:00
if ( incomplete ) {
2023-10-22 22:53:08 +03:00
slot . has_next_token = true ;
}
// check the limits
2024-03-07 11:41:53 +02:00
if ( slot . n_decoded > 0 && slot . has_next_token && ! slot . has_budget ( params )) {
slot . stopped_limit = true ;
2023-10-22 22:53:08 +03:00
slot . has_next_token = false ;
2024-03-07 11:41:53 +02:00
2024-09-15 20:46:12 +03:00
SLT_DBG ( slot , "stopped by limit, n_decoded = %d, n_predict = %d \n " , slot . n_decoded , slot . params . n_predict );
2023-10-22 22:53:08 +03:00
}
2024-10-18 07:32:19 +03:00
if ( slot . has_new_line ) {
// if we have already seen a new line, we stop after a certain time limit
if ( slot . params . t_max_predict_ms > 0 && ( ggml_time_us () - slot . t_start_generation > 1000.0f * slot . params . t_max_predict_ms )) {
slot . stopped_limit = true ;
slot . has_next_token = false ;
2024-10-12 16:14:27 +03:00
2024-10-18 07:32:19 +03:00
SLT_DBG ( slot , "stopped by time limit, n_decoded = %d, t_max_predict_ms = %d ms \n " , slot . n_decoded , ( int ) slot . params . t_max_predict_ms );
}
// require that each new line has a whitespace prefix (i.e. indentation) of at least slot.params.n_indent
if ( slot . params . n_indent > 0 ) {
// check the current indentation
// TODO: improve by not doing it more than once for each new line
if ( slot . last_nl_pos > 0 ) {
size_t pos = slot . last_nl_pos ;
int n_indent = 0 ;
while ( pos < slot . generated_text . size () && ( slot . generated_text [ pos ] == ' ' || slot . generated_text [ pos ] == '\t' )) {
n_indent ++ ;
pos ++ ;
}
if ( pos < slot . generated_text . size () && n_indent < slot . params . n_indent ) {
slot . stopped_limit = true ;
slot . has_next_token = false ;
// cut the last line
slot . generated_text . erase ( pos , std :: string :: npos );
SLT_DBG ( slot , "stopped by indentation limit, n_decoded = %d, n_indent = %d \n " , slot . n_decoded , n_indent );
}
}
// find the next new line
{
const size_t pos = slot . generated_text . find ( '\n' , slot . last_nl_pos );
if ( pos != std :: string :: npos ) {
slot . last_nl_pos = pos + 1 ;
}
}
}
2024-10-12 16:14:27 +03:00
}
// check if there is a new line in the generated text
if ( result . text_to_send . find ( '\n' ) != std :: string :: npos ) {
slot . has_new_line = true ;
}
2024-09-23 22:23:54 +02:00
// if context shift is disabled, we stop when it reaches the context limit
2024-10-12 16:06:31 +03:00
if ( slot . n_past >= slot . n_ctx ) {
2024-09-23 22:23:54 +02:00
slot . truncated = true ;
slot . stopped_limit = true ;
slot . has_next_token = false ;
2024-10-12 16:06:31 +03:00
SLT_DBG ( slot , "stopped due to running out of context capacity, n_past = %d, n_prompt_tokens = %d, n_decoded = %d, n_ctx = %d \n " ,
slot . n_decoded , slot . n_prompt_tokens , slot . n_past , slot . n_ctx );
2024-09-23 22:23:54 +02:00
}
2024-04-21 13:50:41 +02:00
if ( llama_token_is_eog ( model , result . tok )) {
2024-03-07 11:41:53 +02:00
slot . stopped_eos = true ;
2023-10-22 22:53:08 +03:00
slot . has_next_token = false ;
2024-03-07 11:41:53 +02:00
2024-09-15 20:46:12 +03:00
SLT_DBG ( slot , "%s" , "stopped by EOS \n " );
2023-06-17 07:53:04 -04:00
}
2024-09-15 20:46:12 +03:00
const auto n_ctx_train = llama_n_ctx_train ( model );
2024-10-12 16:06:31 +03:00
if ( slot . params . n_predict < 1 && slot . n_predict < 1 && slot . n_prompt_tokens + slot . n_decoded >= n_ctx_train ) {
2024-04-26 12:15:30 +02:00
slot . truncated = true ;
slot . stopped_limit = true ;
slot . has_next_token = false ; // stop prediction
2024-09-15 20:46:12 +03:00
SLT_WRN ( slot ,
2024-10-12 16:06:31 +03:00
"n_predict (%d) is set for infinite generation. "
2024-09-15 20:46:12 +03:00
"Limiting generated tokens to n_ctx_train (%d) to avoid EOS-less generation infinite loop \n " ,
slot . params . n_predict , n_ctx_train );
2024-04-26 12:15:30 +02:00
}
2024-10-12 08:21:51 +03:00
SLT_DBG ( slot , "n_decoded = %d, n_remaining = %d, next token: %5d '%s' \n " , slot . n_decoded , slot . n_remaining , result . tok , token_str . c_str ());
2023-06-17 07:53:04 -04:00
2023-10-22 22:53:08 +03:00
return slot . has_next_token ; // continue
2023-06-17 07:53:04 -04:00
}
2023-06-20 01:12:39 +03:00
2024-03-07 11:41:53 +02:00
json get_formated_generation ( const server_slot & slot ) const {
2024-09-07 15:16:19 +03:00
std :: vector < std :: string > samplers ;
samplers . reserve ( slot . sparams . samplers . size ());
for ( const auto & sampler : slot . sparams . samplers ) {
2024-10-10 22:57:42 +02:00
samplers . emplace_back ( common_sampler_type_to_str ( sampler ));
2024-02-16 11:33:25 +00:00
}
2023-10-22 22:53:08 +03:00
return json {
2024-03-07 11:41:53 +02:00
{ "n_ctx" , slot . n_ctx },
2024-08-15 15:28:05 +08:00
{ "n_predict" , slot . n_predict }, // Server configured n_predict
2024-03-07 11:41:53 +02:00
{ "model" , params . model_alias },
2024-05-19 16:06:33 +02:00
{ "seed" , slot . sparams . seed },
2024-10-10 22:57:42 +02:00
{ "seed_cur" , slot . smpl ? common_sampler_get_seed ( slot . smpl ) : 0 },
2024-03-07 11:41:53 +02:00
{ "temperature" , slot . sparams . temp },
{ "dynatemp_range" , slot . sparams . dynatemp_range },
{ "dynatemp_exponent" , slot . sparams . dynatemp_exponent },
{ "top_k" , slot . sparams . top_k },
{ "top_p" , slot . sparams . top_p },
{ "min_p" , slot . sparams . min_p },
2024-10-15 15:54:55 +05:00
{ "xtc_probability" , slot . sparams . xtc_probability },
{ "xtc_threshold" , slot . sparams . xtc_threshold },
2024-09-07 15:16:19 +03:00
{ "typical_p" , slot . sparams . typ_p },
2024-03-07 11:41:53 +02:00
{ "repeat_last_n" , slot . sparams . penalty_last_n },
{ "repeat_penalty" , slot . sparams . penalty_repeat },
{ "presence_penalty" , slot . sparams . penalty_present },
{ "frequency_penalty" , slot . sparams . penalty_freq },
2024-10-25 10:07:34 -06:00
{ "dry_multiplier" , slot . sparams . dry_multiplier },
{ "dry_base" , slot . sparams . dry_base },
{ "dry_allowed_length" , slot . sparams . dry_allowed_length },
{ "dry_penalty_last_n" , slot . sparams . dry_penalty_last_n },
{ "dry_sequence_breakers" , slot . sparams . dry_sequence_breakers },
2024-03-07 11:41:53 +02:00
{ "mirostat" , slot . sparams . mirostat },
{ "mirostat_tau" , slot . sparams . mirostat_tau },
{ "mirostat_eta" , slot . sparams . mirostat_eta },
{ "penalize_nl" , slot . sparams . penalize_nl },
{ "stop" , slot . params . antiprompt },
2024-08-15 15:28:05 +08:00
{ "max_tokens" , slot . params . n_predict }, // User configured n_predict
2024-03-22 19:12:05 +08:00
{ "n_keep" , slot . params . n_keep },
2024-03-26 16:47:43 +08:00
{ "n_discard" , slot . params . n_discard },
2024-09-07 15:16:19 +03:00
{ "ignore_eos" , slot . sparams . ignore_eos },
2024-03-07 11:41:53 +02:00
{ "stream" , slot . params . stream },
2024-09-07 15:16:19 +03:00
//{"logit_bias", slot.sparams.logit_bias},
2024-03-07 11:41:53 +02:00
{ "n_probs" , slot . sparams . n_probs },
{ "min_keep" , slot . sparams . min_keep },
{ "grammar" , slot . sparams . grammar },
2024-09-07 15:16:19 +03:00
{ "samplers" , samplers },
2023-10-22 22:53:08 +03:00
};
}
2024-03-11 10:56:41 +01:00
void send_error ( const server_task & task , const std :: string & error , const enum error_type type = ERROR_TYPE_SERVER ) {
2024-09-02 17:11:51 +02:00
send_error ( task . id , error , type );
2024-03-11 10:56:41 +01:00
}
void send_error ( const server_slot & slot , const std :: string & error , const enum error_type type = ERROR_TYPE_SERVER ) {
2024-09-02 17:11:51 +02:00
send_error ( slot . id_task , error , type );
2024-03-11 10:56:41 +01:00
}
2024-09-02 17:11:51 +02:00
void send_error ( const int id_task , const std :: string & error , const enum error_type type = ERROR_TYPE_SERVER ) {
2024-09-15 20:46:12 +03:00
SRV_ERR ( "task id = %d, error: %s \n " , id_task , error . c_str ());
2023-10-22 22:53:08 +03:00
2024-03-07 11:41:53 +02:00
server_task_result res ;
2024-03-11 10:56:41 +01:00
res . id = id_task ;
2024-03-07 11:41:53 +02:00
res . stop = false ;
res . error = true ;
2024-03-11 10:56:41 +01:00
res . data = format_error_response ( error , type );
2024-03-07 11:41:53 +02:00
queue_results . send ( res );
}
void send_partial_response ( server_slot & slot , completion_token_output tkn ) {
server_task_result res ;
res . id = slot . id_task ;
res . error = false ;
res . stop = false ;
res . data = json {
2023-10-22 22:53:08 +03:00
{ "content" , tkn . text_to_send },
{ "stop" , false },
2024-03-07 11:41:53 +02:00
{ "id_slot" , slot . id },
2024-09-02 17:11:51 +02:00
{ "multimodal" , false },
{ "index" , slot . index },
2023-10-22 22:53:08 +03:00
};
2024-03-07 11:41:53 +02:00
if ( slot . sparams . n_probs > 0 ) {
2024-10-24 21:51:22 +02:00
const llama_tokens to_send_toks = common_tokenize ( ctx , tkn . text_to_send , false );
2024-03-07 11:41:53 +02:00
const size_t probs_pos = std :: min ( slot . n_sent_token_probs , slot . generated_token_probs . size ());
const size_t probs_stop_pos = std :: min ( slot . n_sent_token_probs + to_send_toks . size (), slot . generated_token_probs . size ());
std :: vector < completion_token_output > probs_output ;
if ( probs_pos < probs_stop_pos ) {
probs_output = std :: vector < completion_token_output > (
slot . generated_token_probs . begin () + probs_pos ,
slot . generated_token_probs . begin () + probs_stop_pos );
2023-10-22 22:53:08 +03:00
}
2024-02-29 21:42:11 +01:00
slot . n_sent_token_probs = probs_stop_pos ;
2024-03-07 11:41:53 +02:00
res . data [ "completion_probabilities" ] = probs_vector_to_json ( ctx , probs_output );
2023-10-22 22:53:08 +03:00
}
2024-03-07 11:41:53 +02:00
if ( slot . oaicompat ) {
res . data [ "oaicompat_token_ctr" ] = slot . n_decoded ;
res . data [ "model" ] = slot . oaicompat_model ;
2023-11-25 11:29:06 +02:00
}
2024-01-26 13:42:20 +01:00
queue_results . send ( res );
2023-10-22 22:53:08 +03:00
}
2024-03-07 11:41:53 +02:00
void send_final_response ( const server_slot & slot ) {
server_task_result res ;
res . id = slot . id_task ;
res . error = false ;
res . stop = true ;
res . data = json {
2023-10-22 22:53:08 +03:00
{ "content" , ! slot . params . stream ? slot . generated_text : "" },
2024-03-07 11:41:53 +02:00
{ "id_slot" , slot . id },
2023-10-22 22:53:08 +03:00
{ "stop" , true },
{ "model" , params . model_alias },
{ "tokens_predicted" , slot . n_decoded },
2024-02-29 21:42:11 +01:00
{ "tokens_evaluated" , slot . n_prompt_tokens },
2023-10-22 22:53:08 +03:00
{ "generation_settings" , get_formated_generation ( slot )},
2024-10-24 21:51:22 +02:00
{ "prompt" , common_detokenize ( ctx , slot . prompt_tokens )},
2024-10-12 16:14:27 +03:00
{ "has_new_line" , slot . has_new_line },
2023-10-22 22:53:08 +03:00
{ "truncated" , slot . truncated },
{ "stopped_eos" , slot . stopped_eos },
{ "stopped_word" , slot . stopped_word },
{ "stopped_limit" , slot . stopped_limit },
{ "stopping_word" , slot . stopping_word },
{ "tokens_cached" , slot . n_past },
2024-09-02 17:11:51 +02:00
{ "timings" , slot . get_formated_timings ()},
{ "index" , slot . index },
2023-10-22 22:53:08 +03:00
};
2024-03-07 11:41:53 +02:00
if ( slot . sparams . n_probs > 0 ) {
std :: vector < completion_token_output > probs ;
if ( ! slot . params . stream && slot . stopped_word ) {
2024-10-24 21:51:22 +02:00
const llama_tokens stop_word_toks = common_tokenize ( ctx , slot . stopping_word , false );
2024-03-07 11:41:53 +02:00
2024-05-04 12:06:40 +03:00
size_t safe_offset = std :: min ( slot . generated_token_probs . size (), stop_word_toks . size ());
2023-10-22 22:53:08 +03:00
probs = std :: vector < completion_token_output > (
2024-03-07 11:41:53 +02:00
slot . generated_token_probs . begin (),
2024-05-04 12:06:40 +03:00
slot . generated_token_probs . end () - safe_offset );
2024-03-07 11:41:53 +02:00
} else {
probs = std :: vector < completion_token_output > (
slot . generated_token_probs . begin (),
slot . generated_token_probs . end ());
2023-10-22 22:53:08 +03:00
}
2024-03-07 11:41:53 +02:00
res . data [ "completion_probabilities" ] = probs_vector_to_json ( ctx , probs );
2023-10-22 22:53:08 +03:00
}
2024-03-07 11:41:53 +02:00
if ( slot . oaicompat ) {
res . data [ "oaicompat_token_ctr" ] = slot . n_decoded ;
res . data [ "model" ] = slot . oaicompat_model ;
2023-11-25 11:29:06 +02:00
}
2024-01-26 13:42:20 +01:00
queue_results . send ( res );
2023-10-22 22:53:08 +03:00
}
2024-03-07 11:41:53 +02:00
void send_embedding ( const server_slot & slot , const llama_batch & batch ) {
server_task_result res ;
res . id = slot . id_task ;
res . error = false ;
res . stop = true ;
2023-10-22 22:53:08 +03:00
const int n_embd = llama_n_embd ( model );
2024-03-04 22:31:20 +02:00
2024-03-09 21:27:58 +09:00
std :: vector < float > embd_res ( n_embd , 0.0f );
2024-03-07 11:41:53 +02:00
for ( int i = 0 ; i < batch . n_tokens ; ++ i ) {
2024-03-08 17:31:00 -05:00
if ( ! batch . logits [ i ] || batch . seq_id [ i ][ 0 ] != slot . id + 1 ) {
2024-03-07 11:41:53 +02:00
continue ;
}
const float * embd = llama_get_embeddings_seq ( ctx , batch . seq_id [ i ][ 0 ]);
if ( embd == NULL ) {
embd = llama_get_embeddings_ith ( ctx , i );
}
if ( embd == NULL ) {
2024-09-15 20:46:12 +03:00
SLT_ERR ( slot , "failed to get embeddings, token = %d, seq_id = %d \n " , batch . token [ i ], batch . seq_id [ i ][ 0 ]);
2024-03-07 11:41:53 +02:00
res . data = json {
{ "embedding" , std :: vector < float > ( n_embd , 0.0f )},
2024-09-28 17:42:03 +03:00
{ "index" , slot . index },
2024-03-07 11:41:53 +02:00
};
continue ;
}
2024-10-10 22:57:42 +02:00
common_embd_normalize ( embd , embd_res . data (), n_embd );
2024-03-09 21:27:58 +09:00
2024-03-07 11:41:53 +02:00
res . data = json {
2024-03-09 21:27:58 +09:00
{ "embedding" , embd_res },
2024-09-02 17:11:51 +02:00
{ "index" , slot . index },
2023-10-22 22:53:08 +03:00
};
2023-06-20 01:12:39 +03:00
}
2024-03-04 22:31:20 +02:00
2024-09-15 20:46:12 +03:00
SLT_DBG ( slot , "%s" , "sending embeddings \n " );
2024-01-26 13:42:20 +01:00
queue_results . send ( res );
2023-10-22 22:53:08 +03:00
}
2024-09-28 17:42:03 +03:00
void send_rerank ( const server_slot & slot , const llama_batch & batch ) {
server_task_result res ;
res . id = slot . id_task ;
res . error = false ;
res . stop = true ;
for ( int i = 0 ; i < batch . n_tokens ; ++ i ) {
if ( ! batch . logits [ i ] || batch . seq_id [ i ][ 0 ] != slot . id + 1 ) {
continue ;
}
const float * embd = llama_get_embeddings_seq ( ctx , batch . seq_id [ i ][ 0 ]);
if ( embd == NULL ) {
embd = llama_get_embeddings_ith ( ctx , i );
}
if ( embd == NULL ) {
SLT_ERR ( slot , "failed to get embeddings, token = %d, seq_id = %d \n " , batch . token [ i ], batch . seq_id [ i ][ 0 ]);
res . data = json {
{ "index" , slot . index },
{ "score" , - 1e6 },
};
continue ;
}
res . data = json {
{ "index" , slot . index },
{ "score" , embd [ 0 ]},
};
}
SLT_DBG ( slot , "sending rerank result, res = '%s' \n " , res . data . dump (). c_str ());
queue_results . send ( res );
}
2024-09-02 17:11:51 +02:00
//
// Functions to create new task(s) and receive result(s)
//
2023-11-30 17:25:04 -05:00
2024-10-24 21:51:22 +02:00
// break the input "prompt" into multiple tasks if needed, then format and tokenize the input prompt(s)
std :: vector < server_task > create_tasks_inference ( json data , server_task_inf_type inf_type ) {
2024-09-02 17:11:51 +02:00
std :: vector < server_task > tasks ;
2024-10-24 21:51:22 +02:00
auto create_task = [ & ]( json & task_data , llama_tokens & prompt_tokens ) {
SRV_DBG ( "create task, n_tokens = %d \n " , ( int ) prompt_tokens . size ());
2024-09-02 17:11:51 +02:00
server_task task ;
2024-10-24 21:51:22 +02:00
task . id = queue_tasks . get_new_id ();
task . inf_type = inf_type ;
task . type = SERVER_TASK_TYPE_INFERENCE ;
task . data = task_data ;
task . prompt_tokens = std :: move ( prompt_tokens );
2024-09-02 17:11:51 +02:00
tasks . push_back ( std :: move ( task ));
};
static constexpr const char * error_msg = " \" prompt \" must be a string, an array of token ids or an array of prompts" ;
if ( ! data . contains ( "prompt" )) {
throw std :: runtime_error ( error_msg );
}
2024-10-24 21:51:22 +02:00
// because llama_tokenize api is thread-safe, we can tokenize the prompt from HTTP thread
bool add_special = inf_type != SERVER_TASK_INF_TYPE_RERANK && inf_type != SERVER_TASK_INF_TYPE_INFILL ;
std :: vector < llama_tokens > tokenized_prompts = tokenize_input_prompts ( ctx , data . at ( "prompt" ), add_special , true );
switch ( inf_type ) {
case SERVER_TASK_INF_TYPE_RERANK :
{
// prompts[0] is the question
// the rest are the answers/documents
GGML_ASSERT ( tokenized_prompts . size () > 1 );
SRV_DBG ( "creating rerank tasks, n_prompts = %d \n " , ( int ) tokenized_prompts . size () - 1 );
for ( size_t i = 1 ; i < tokenized_prompts . size (); i ++ ) {
data [ "index" ] = i - 1 ;
auto tokens = format_rerank ( model , tokenized_prompts [ 0 ], tokenized_prompts [ i ]);
create_task ( data , tokens );
}
} break ;
case SERVER_TASK_INF_TYPE_INFILL :
{
SRV_DBG ( "creating infill tasks, n_prompts = %d \n " , ( int ) tokenized_prompts . size ());
for ( size_t i = 0 ; i < tokenized_prompts . size (); i ++ ) {
2024-09-28 17:42:03 +03:00
data [ "index" ] = i ;
2024-10-24 21:51:22 +02:00
auto tokens = format_infill (
ctx ,
data . at ( "input_prefix" ),
data . at ( "input_suffix" ),
data . at ( "input_extra" ),
params . n_batch ,
params . n_predict ,
slots [ 0 ]. n_ctx , // TODO: there should be a better way
params . spm_infill ,
tokenized_prompts [ i ]
);
create_task ( data , tokens );
}
} break ;
default :
{
SRV_DBG ( "creating multi-prompt tasks, n_prompts = %d \n " , ( int ) tokenized_prompts . size ());
for ( size_t i = 0 ; i < tokenized_prompts . size (); i ++ ) {
data [ "index" ] = i ;
create_task ( data , tokenized_prompts [ i ]);
2024-09-28 17:42:03 +03:00
}
2024-09-02 17:11:51 +02:00
}
}
return tasks ;
}
void cancel_tasks ( const std :: unordered_set < int > & id_tasks ) {
std :: vector < server_task > cancel_tasks ;
cancel_tasks . reserve ( id_tasks . size ());
for ( const auto & id_task : id_tasks ) {
2024-09-15 20:46:12 +03:00
SRV_WRN ( "cancel task, id_task = %d \n " , id_task );
2024-09-02 17:11:51 +02:00
server_task task ;
task . type = SERVER_TASK_TYPE_CANCEL ;
task . id_target = id_task ;
cancel_tasks . push_back ( task );
queue_results . remove_waiting_task_id ( id_task );
}
// push to beginning of the queue, so it has highest priority
queue_tasks . post ( cancel_tasks , true );
}
2024-10-24 21:51:22 +02:00
// receive the results from task(s) created by create_tasks_inference
2024-09-15 20:46:12 +03:00
void receive_cmpl_results (
const std :: unordered_set < int > & id_tasks ,
const std :: function < void ( std :: vector < server_task_result >& ) > & result_handler ,
const std :: function < void ( json ) > & error_handler ) {
2024-09-02 17:11:51 +02:00
// TODO: currently, there is no way to detect the client has cancelled the request
std :: vector < server_task_result > results ( id_tasks . size ());
for ( size_t i = 0 ; i < id_tasks . size (); i ++ ) {
server_task_result result = queue_results . recv ( id_tasks );
if ( result . error ) {
error_handler ( result . data );
cancel_tasks ( id_tasks );
2024-09-23 22:23:54 +02:00
return ;
2024-09-02 17:11:51 +02:00
}
2024-09-28 17:42:03 +03:00
const size_t idx = result . data [ "index" ];
GGML_ASSERT ( idx < results . size () && "index out of range" );
2024-09-02 17:11:51 +02:00
results [ idx ] = result ;
}
result_handler ( results );
}
2024-10-24 21:51:22 +02:00
// receive the results from task(s) created by create_tasks_inference, in stream mode
2024-09-15 20:46:12 +03:00
void receive_cmpl_results_stream (
const std :: unordered_set < int > & id_tasks , const
std :: function < bool ( server_task_result & ) > & result_handler , const
std :: function < void ( json ) > & error_handler ) {
2024-09-02 17:11:51 +02:00
size_t n_finished = 0 ;
while ( true ) {
server_task_result result = queue_results . recv ( id_tasks );
if ( ! result_handler ( result )) {
cancel_tasks ( id_tasks );
break ;
}
if ( result . error ) {
error_handler ( result . data );
cancel_tasks ( id_tasks );
break ;
}
if ( result . stop ) {
if ( ++ n_finished == id_tasks . size ()) {
2024-02-06 08:16:23 +00:00
break ;
}
}
}
2023-10-22 22:53:08 +03:00
}
2024-09-02 17:11:51 +02:00
//
// Functions to process the task
//
2023-11-30 17:25:04 -05:00
2024-03-07 11:41:53 +02:00
void process_single_task ( const server_task & task ) {
switch ( task . type ) {
2024-10-24 21:51:22 +02:00
case SERVER_TASK_TYPE_INFERENCE :
2023-11-30 17:25:04 -05:00
{
2024-06-10 14:59:55 +03:00
const int id_slot = json_value ( task . data , "id_slot" , - 1 );
2024-06-08 07:50:31 +00:00
2024-11-01 13:33:14 +00:00
server_slot * slot = id_slot != - 1 ? get_slot_by_id ( id_slot ) : get_available_slot ( task );
2024-06-08 07:50:31 +00:00
2024-03-07 11:41:53 +02:00
if ( slot == nullptr ) {
// if no slot is available, we defer this task for processing later
2024-09-15 20:46:12 +03:00
SRV_DBG ( "no slot is available, defer task, id_task = %d \n " , task . id );
2024-03-07 11:41:53 +02:00
queue_tasks . defer ( task );
2024-01-26 13:42:20 +01:00
break ;
}
2024-09-06 23:21:29 +02:00
if ( slot -> is_processing ()) {
2024-06-08 07:50:31 +00:00
// if requested slot is unavailable, we defer this task for processing later
2024-09-15 20:46:12 +03:00
SRV_DBG ( "requested slot is unavailable, defer task, id_task = %d \n " , task . id );
2024-06-08 07:50:31 +00:00
queue_tasks . defer ( task );
break ;
}
2024-01-13 09:20:46 -05:00
2024-03-07 11:41:53 +02:00
slot -> reset ();
2023-11-30 17:25:04 -05:00
2024-10-24 21:51:22 +02:00
slot -> id_task = task . id ;
slot -> inf_type = task . inf_type ;
slot -> index = json_value ( task . data , "index" , 0 );
slot -> prompt_tokens = std :: move ( task . prompt_tokens );
2024-01-26 13:42:20 +01:00
2024-03-11 10:56:41 +01:00
if ( ! launch_slot_with_task ( * slot , task )) {
2024-09-15 20:46:12 +03:00
SRV_ERR ( "failed to launch slot with task, id_task = %d \n " , task . id );
2024-01-26 13:42:20 +01:00
break ;
}
2024-03-07 11:41:53 +02:00
} break ;
case SERVER_TASK_TYPE_CANCEL :
{
// release slot linked with the task id
for ( auto & slot : slots ) {
if ( slot . id_task == task . id_target ) {
slot . release ();
break ;
}
2024-02-24 12:28:55 +01:00
}
2024-03-07 11:41:53 +02:00
} break ;
case SERVER_TASK_TYPE_NEXT_RESPONSE :
{
// do nothing
} break ;
case SERVER_TASK_TYPE_METRICS :
{
json slots_data = json :: array ();
int n_idle_slots = 0 ;
int n_processing_slots = 0 ;
for ( server_slot & slot : slots ) {
json slot_data = get_formated_generation ( slot );
slot_data [ "id" ] = slot . id ;
slot_data [ "id_task" ] = slot . id_task ;
slot_data [ "state" ] = slot . state ;
2024-10-24 21:51:22 +02:00
slot_data [ "prompt" ] = common_detokenize ( ctx , slot . prompt_tokens );
2024-03-07 11:41:53 +02:00
slot_data [ "next_token" ] = {
{ "has_next_token" , slot . has_next_token },
2024-10-12 16:14:27 +03:00
{ "has_new_line" , slot . has_new_line },
2024-03-07 11:41:53 +02:00
{ "n_remain" , slot . n_remaining },
{ "n_decoded" , slot . n_decoded },
{ "stopped_eos" , slot . stopped_eos },
{ "stopped_word" , slot . stopped_word },
{ "stopped_limit" , slot . stopped_limit },
{ "stopping_word" , slot . stopping_word },
};
if ( slot_data [ "state" ] == SLOT_STATE_IDLE ) {
n_idle_slots ++ ;
} else {
n_processing_slots ++ ;
}
slots_data . push_back ( slot_data );
}
2024-09-15 20:46:12 +03:00
SRV_DBG ( "n_idle_slots = %d, n_processing_slots = %d \n " , n_idle_slots , n_processing_slots );
2024-03-07 11:41:53 +02:00
server_task_result res ;
res . id = task . id ;
res . stop = true ;
res . error = false ;
res . data = {
2024-02-25 13:49:43 +01:00
{ "idle" , n_idle_slots },
{ "processing" , n_processing_slots },
{ "deferred" , queue_tasks . queue_tasks_deferred . size () },
2024-03-08 12:25:04 +01:00
{ "t_start" , metrics . t_start },
2024-02-25 13:49:43 +01:00
{ "n_prompt_tokens_processed_total" , metrics . n_prompt_tokens_processed_total },
2024-03-08 12:25:04 +01:00
{ "t_tokens_generation_total" , metrics . t_tokens_generation_total },
2024-02-25 13:49:43 +01:00
{ "n_tokens_predicted_total" , metrics . n_tokens_predicted_total },
2024-03-08 12:25:04 +01:00
{ "t_prompt_processing_total" , metrics . t_prompt_processing_total },
2024-02-25 13:49:43 +01:00
{ "n_prompt_tokens_processed" , metrics . n_prompt_tokens_processed },
{ "t_prompt_processing" , metrics . t_prompt_processing },
{ "n_tokens_predicted" , metrics . n_tokens_predicted },
{ "t_tokens_generation" , metrics . t_tokens_generation },
2024-09-06 23:21:29 +02:00
{ "n_decode_total" , metrics . n_decode_total },
{ "n_busy_slots_total" , metrics . n_busy_slots_total },
2024-02-29 21:42:11 +01:00
{ "kv_cache_tokens_count" , llama_get_kv_cache_token_count ( ctx )},
{ "kv_cache_used_cells" , llama_get_kv_cache_used_cells ( ctx )},
2024-02-25 13:49:43 +01:00
2024-02-29 21:42:11 +01:00
{ "slots" , slots_data },
2024-03-07 11:41:53 +02:00
};
2024-03-08 12:25:04 +01:00
if ( json_value ( task . data , "reset_bucket" , false )) {
metrics . reset_bucket ();
}
2024-03-07 11:41:53 +02:00
queue_results . send ( res );
} break ;
2024-04-08 20:43:30 +08:00
case SERVER_TASK_TYPE_SLOT_SAVE :
{
2024-05-08 21:53:08 +02:00
int id_slot = task . data . at ( "id_slot" );
2024-06-08 07:50:31 +00:00
server_slot * slot = get_slot_by_id ( id_slot );
2024-04-08 20:43:30 +08:00
if ( slot == nullptr ) {
send_error ( task , "Invalid slot ID" , ERROR_TYPE_INVALID_REQUEST );
break ;
}
2024-09-06 23:21:29 +02:00
if ( slot -> is_processing ()) {
2024-06-08 07:50:31 +00:00
// if requested slot is unavailable, we defer this task for processing later
2024-09-15 20:46:12 +03:00
SRV_DBG ( "requested slot is unavailable, defer task, id_task = %d \n " , task . id );
2024-06-08 07:50:31 +00:00
queue_tasks . defer ( task );
break ;
}
2024-04-08 20:43:30 +08:00
const size_t token_count = slot -> cache_tokens . size ();
const int64_t t_start = ggml_time_us ();
2024-05-08 21:53:08 +02:00
std :: string filename = task . data . at ( "filename" );
std :: string filepath = task . data . at ( "filepath" );
2024-04-08 20:43:30 +08:00
const size_t nwrite = llama_state_seq_save_file ( ctx , filepath . c_str (), slot -> id + 1 , slot -> cache_tokens . data (), token_count );
const int64_t t_end = ggml_time_us ();
const double t_save_ms = ( t_end - t_start ) / 1000.0 ;
server_task_result result ;
result . id = task . id ;
result . stop = true ;
result . error = false ;
result . data = json {
{ "id_slot" , id_slot },
{ "filename" , filename },
{ "n_saved" , token_count }, // tokens saved
{ "n_written" , nwrite }, // bytes written
{ "timings" , {
{ "save_ms" , t_save_ms }
} }
};
queue_results . send ( result );
} break ;
case SERVER_TASK_TYPE_SLOT_RESTORE :
{
2024-05-08 21:53:08 +02:00
int id_slot = task . data . at ( "id_slot" );
2024-06-08 07:50:31 +00:00
server_slot * slot = get_slot_by_id ( id_slot );
2024-04-08 20:43:30 +08:00
if ( slot == nullptr ) {
send_error ( task , "Invalid slot ID" , ERROR_TYPE_INVALID_REQUEST );
break ;
}
2024-09-06 23:21:29 +02:00
if ( slot -> is_processing ()) {
2024-06-08 07:50:31 +00:00
// if requested slot is unavailable, we defer this task for processing later
2024-09-15 20:46:12 +03:00
SRV_DBG ( "requested slot is unavailable, defer task, id_task = %d \n " , task . id );
2024-06-08 07:50:31 +00:00
queue_tasks . defer ( task );
break ;
}
2024-04-08 20:43:30 +08:00
const int64_t t_start = ggml_time_us ();
2024-05-08 21:53:08 +02:00
std :: string filename = task . data . at ( "filename" );
std :: string filepath = task . data . at ( "filepath" );
2024-04-08 20:43:30 +08:00
slot -> cache_tokens . resize ( slot -> n_ctx );
size_t token_count = 0 ;
size_t nread = llama_state_seq_load_file ( ctx , filepath . c_str (), slot -> id + 1 , slot -> cache_tokens . data (), slot -> cache_tokens . size (), & token_count );
if ( nread == 0 ) {
slot -> cache_tokens . resize ( 0 );
send_error ( task , "Unable to restore slot, no available space in KV cache or invalid slot save file" , ERROR_TYPE_INVALID_REQUEST );
break ;
}
slot -> cache_tokens . resize ( token_count );
const int64_t t_end = ggml_time_us ();
const double t_restore_ms = ( t_end - t_start ) / 1000.0 ;
server_task_result result ;
result . id = task . id ;
result . stop = true ;
result . error = false ;
result . data = json {
{ "id_slot" , id_slot },
{ "filename" , filename },
{ "n_restored" , token_count }, // tokens restored
{ "n_read" , nread }, // bytes read
{ "timings" , {
{ "restore_ms" , t_restore_ms }
} }
};
queue_results . send ( result );
} break ;
case SERVER_TASK_TYPE_SLOT_ERASE :
{
2024-05-08 21:53:08 +02:00
int id_slot = task . data . at ( "id_slot" );
2024-06-08 07:50:31 +00:00
server_slot * slot = get_slot_by_id ( id_slot );
2024-04-08 20:43:30 +08:00
if ( slot == nullptr ) {
send_error ( task , "Invalid slot ID" , ERROR_TYPE_INVALID_REQUEST );
break ;
}
2024-09-06 23:21:29 +02:00
if ( slot -> is_processing ()) {
2024-06-08 07:50:31 +00:00
// if requested slot is unavailable, we defer this task for processing later
2024-09-15 20:46:12 +03:00
SRV_DBG ( "requested slot is unavailable, defer task, id_task = %d \n " , task . id );
2024-06-08 07:50:31 +00:00
queue_tasks . defer ( task );
break ;
}
2024-04-08 20:43:30 +08:00
// Erase token cache
const size_t n_erased = slot -> cache_tokens . size ();
llama_kv_cache_seq_rm ( ctx , slot -> id + 1 , - 1 , - 1 );
slot -> cache_tokens . clear ();
server_task_result result ;
result . id = task . id ;
result . stop = true ;
result . error = false ;
result . data = json {
{ "id_slot" , id_slot },
{ "n_erased" , n_erased }
};
queue_results . send ( result );
} break ;
2024-08-06 17:33:39 +02:00
case SERVER_TASK_TYPE_SET_LORA :
{
2024-10-10 22:57:42 +02:00
common_lora_adapters_apply ( ctx , loras );
2024-08-06 17:33:39 +02:00
server_task_result result ;
result . id = task . id ;
2024-08-15 08:21:57 +02:00
result . stop = true ;
result . error = false ;
2024-08-06 17:33:39 +02:00
result . data = json {{ "success" , true }};
queue_results . send ( result );
} break ;
2023-11-30 17:25:04 -05:00
}
2024-01-26 13:42:20 +01:00
}
2024-01-13 09:20:46 -05:00
2024-03-11 10:56:41 +01:00
void update_slots () {
2024-03-07 11:41:53 +02:00
// check if all slots are idle
2023-10-22 22:53:08 +03:00
{
2024-03-07 11:41:53 +02:00
bool all_idle = true ;
for ( auto & slot : slots ) {
2024-09-06 23:21:29 +02:00
if ( slot . is_processing ()) {
2024-03-07 11:41:53 +02:00
all_idle = false ;
break ;
}
}
if ( all_idle ) {
2024-09-15 20:46:12 +03:00
SRV_INF ( "%s" , "all slots are idle \n " );
2024-10-12 14:51:54 +03:00
if ( clean_kv_cache ) {
2024-03-07 11:41:53 +02:00
kv_cache_clear ();
}
2024-03-11 10:56:41 +01:00
return ;
2024-03-07 11:41:53 +02:00
}
}
{
2024-09-15 20:46:12 +03:00
SRV_DBG ( "%s" , "posting NEXT_RESPONSE \n " );
2024-03-07 11:41:53 +02:00
server_task task ;
task . type = SERVER_TASK_TYPE_NEXT_RESPONSE ;
task . id_target = - 1 ;
queue_tasks . post ( task );
}
// apply context-shift if needed
// TODO: simplify and improve
for ( server_slot & slot : slots ) {
2024-10-12 16:06:31 +03:00
if ( slot . is_processing () && slot . n_past + 1 >= slot . n_ctx ) {
if ( ! params . ctx_shift ) {
// this check is redundant (for good)
// we should never get here, because generation should already stopped in process_token()
slot . release ();
send_error ( slot , "context shift is disabled" , ERROR_TYPE_SERVER );
continue ;
2023-10-22 22:53:08 +03:00
}
2024-10-12 16:06:31 +03:00
// Shift context
const int n_keep = slot . params . n_keep + add_bos_token ;
const int n_left = slot . n_past - n_keep ;
const int n_discard = slot . params . n_discard ? slot . params . n_discard : ( n_left / 2 );
SLT_WRN ( slot , "slot context shift, n_keep = %d, n_left = %d, n_discard = %d \n " , n_keep , n_left , n_discard );
llama_kv_cache_seq_rm ( ctx , slot . id + 1 , n_keep , n_keep + n_discard );
llama_kv_cache_seq_add ( ctx , slot . id + 1 , n_keep + n_discard , slot . n_past , - n_discard );
if ( slot . params . cache_prompt ) {
for ( size_t i = n_keep + n_discard ; i < slot . cache_tokens . size (); i ++ ) {
slot . cache_tokens [ i - n_discard ] = slot . cache_tokens [ i ];
}
slot . cache_tokens . resize ( slot . cache_tokens . size () - n_discard );
}
slot . n_past -= n_discard ;
slot . truncated = true ;
2023-10-22 22:53:08 +03:00
}
}
2024-03-07 11:41:53 +02:00
// start populating the batch for this iteration
2024-10-10 22:57:42 +02:00
common_batch_clear ( batch );
2023-10-22 22:53:08 +03:00
2024-03-07 11:41:53 +02:00
// frist, add sampled tokens from any ongoing sequences
for ( auto & slot : slots ) {
2024-09-06 23:21:29 +02:00
if ( slot . state != SLOT_STATE_GENERATING ) {
2023-10-22 22:53:08 +03:00
continue ;
}
slot . i_batch = batch . n_tokens ;
2024-10-12 16:06:31 +03:00
common_batch_add ( batch , slot . sampled , slot . n_past , { slot . id + 1 }, true );
2024-03-07 11:41:53 +02:00
2023-10-22 22:53:08 +03:00
slot . n_past += 1 ;
2024-03-07 11:41:53 +02:00
if ( slot . params . cache_prompt ) {
slot . cache_tokens . push_back ( slot . sampled );
}
2024-10-12 14:51:54 +03:00
SLT_DBG ( slot , "slot decode token, n_ctx = %d, n_past = %d, n_cache_tokens = %d, truncated = %d \n " ,
slot . n_ctx , slot . n_past , ( int ) slot . cache_tokens . size (), slot . truncated );
2023-10-22 22:53:08 +03:00
}
// process in chunks of params.n_batch
2024-03-22 13:08:28 +02:00
int32_t n_batch = llama_n_batch ( ctx );
2024-03-13 18:54:21 +01:00
int32_t n_ubatch = llama_n_ubatch ( ctx );
2023-10-22 22:53:08 +03:00
2024-07-12 03:14:12 -05:00
// track if this is an embedding or non-embedding batch
// if we've added sampled tokens above, we are in non-embedding mode
// -1: none, 0: non-embedding, 1: embedding
2024-09-28 17:42:03 +03:00
// TODO: make enum
2024-07-12 03:14:12 -05:00
int32_t batch_type = batch . n_tokens > 0 ? 0 : - 1 ;
2024-03-07 11:41:53 +02:00
// next, batch any pending prompts without exceeding n_batch
if ( params . cont_batching || batch . n_tokens == 0 ) {
for ( auto & slot : slots ) {
// this slot still has a prompt to be processed
2024-10-24 21:51:22 +02:00
if ( slot . state == SLOT_STATE_PROCESSING_PROMPT || slot . state == SLOT_STATE_STARTED ) {
2024-03-07 11:41:53 +02:00
auto & prompt_tokens = slot . prompt_tokens ;
2023-10-22 22:53:08 +03:00
2024-10-24 21:51:22 +02:00
// TODO: maybe move branch to outside of this loop in the future
if ( slot . state == SLOT_STATE_STARTED ) {
2024-03-07 11:41:53 +02:00
slot . t_start_process_prompt = ggml_time_us ();
slot . t_start_generation = 0 ;
2024-10-28 08:49:32 +02:00
2024-03-07 11:41:53 +02:00
slot . n_past = 0 ;
slot . n_prompt_tokens = prompt_tokens . size ();
2024-10-24 21:51:22 +02:00
slot . state = SLOT_STATE_PROCESSING_PROMPT ;
2024-03-07 11:41:53 +02:00
2024-10-24 21:51:22 +02:00
SLT_INF ( slot , "new prompt, n_ctx_slot = %d, n_keep = %d, n_prompt_tokens = %d \n " , slot . n_ctx , slot . params . n_keep , slot . n_prompt_tokens );
2024-03-09 10:30:04 +01:00
2024-10-12 16:14:27 +03:00
// print prompt tokens (for debugging)
if ( 1 ) {
// first 16 tokens (avoid flooding logs)
for ( int i = 0 ; i < std :: min < int > ( 16 , prompt_tokens . size ()); i ++ ) {
SLT_DBG ( slot , "prompt token %3d: %6d '%s' \n " , i , prompt_tokens [ i ], common_token_to_piece ( ctx , prompt_tokens [ i ]). c_str ());
}
} else {
// all
for ( int i = 0 ; i < ( int ) prompt_tokens . size (); i ++ ) {
SLT_DBG ( slot , "prompt token %3d: %6d '%s' \n " , i , prompt_tokens [ i ], common_token_to_piece ( ctx , prompt_tokens [ i ]). c_str ());
}
2024-10-12 08:21:51 +03:00
}
2024-03-09 12:34:18 +02:00
// empty prompt passed -> release the slot and send empty response
if ( prompt_tokens . empty ()) {
2024-09-15 20:46:12 +03:00
SLT_WRN ( slot , "%s" , "empty prompt - releasing slot \n " );
2024-03-09 12:34:18 +02:00
slot . release ();
slot . print_timings ();
send_final_response ( slot );
continue ;
}
2024-10-24 21:51:22 +02:00
if ( slot . inf_type == SERVER_TASK_INF_TYPE_EMBEDDING || slot . inf_type == SERVER_TASK_INF_TYPE_RERANK ) {
2024-03-13 18:54:21 +01:00
if ( slot . n_prompt_tokens > n_ubatch ) {
2024-03-07 11:41:53 +02:00
slot . release ();
2024-05-20 08:56:05 +03:00
send_error ( slot , "input is too large to process. increase the physical batch size" , ERROR_TYPE_SERVER );
2024-03-07 11:41:53 +02:00
continue ;
}
2024-10-25 10:13:46 +03:00
if ( slot . n_prompt_tokens > slot . n_ctx ) {
slot . release ();
send_error ( slot , "input is larger than the max context size. skipping" , ERROR_TYPE_SERVER );
continue ;
}
2024-03-07 11:41:53 +02:00
} else {
2024-09-23 22:23:54 +02:00
if ( ! params . ctx_shift ) {
// if context shift is disabled, we make sure prompt size is smaller than KV size
2024-10-12 16:06:31 +03:00
// TODO: there should be a separate parameter that control prompt truncation
// context shift should be applied only during the generation phase
2024-10-12 14:51:54 +03:00
if ( slot . n_prompt_tokens >= slot . n_ctx ) {
2024-09-23 22:23:54 +02:00
slot . release ();
send_error ( slot , "the request exceeds the available context size. try increasing the context size or enable context shift" , ERROR_TYPE_INVALID_REQUEST );
continue ;
}
}
2024-03-07 11:41:53 +02:00
if ( slot . params . n_keep < 0 ) {
slot . params . n_keep = slot . n_prompt_tokens ;
}
slot . params . n_keep = std :: min ( slot . n_ctx - 4 , slot . params . n_keep );
2024-10-13 18:52:48 +03:00
// if input prompt is too big, truncate it
2024-10-12 16:06:31 +03:00
if ( slot . n_prompt_tokens >= slot . n_ctx ) {
2024-03-07 11:41:53 +02:00
const int n_left = slot . n_ctx - slot . params . n_keep ;
const int n_block_size = n_left / 2 ;
const int erased_blocks = ( slot . n_prompt_tokens - slot . params . n_keep - n_block_size ) / n_block_size ;
2024-10-24 21:51:22 +02:00
llama_tokens new_tokens (
2024-03-07 11:41:53 +02:00
prompt_tokens . begin (),
prompt_tokens . begin () + slot . params . n_keep );
new_tokens . insert (
new_tokens . end (),
prompt_tokens . begin () + slot . params . n_keep + erased_blocks * n_block_size ,
prompt_tokens . end ());
prompt_tokens = std :: move ( new_tokens );
slot . truncated = true ;
slot . n_prompt_tokens = prompt_tokens . size ();
2024-09-15 20:46:12 +03:00
SLT_WRN ( slot , "input truncated, n_ctx = %d, n_keep = %d, n_left = %d, n_prompt_tokens = %d \n " , slot . n_ctx , slot . params . n_keep , n_left , slot . n_prompt_tokens );
2024-03-07 11:41:53 +02:00
GGML_ASSERT ( slot . n_prompt_tokens < slot . n_ctx );
}
2024-10-12 16:06:31 +03:00
if ( slot . params . cache_prompt ) {
2024-03-07 11:41:53 +02:00
// reuse any previously computed tokens that are common with the new prompt
2024-10-13 18:52:48 +03:00
slot . n_past = longest_common_prefix ( slot . cache_tokens , prompt_tokens );
2024-03-07 11:41:53 +02:00
2024-10-13 18:52:48 +03:00
// reuse chunks from the cached prompt by shifting their KV cache in the new position
if ( params . n_cache_reuse > 0 ) {
size_t head_c = slot . n_past ; // cache
size_t head_p = slot . n_past ; // current prompt
SLT_DBG ( slot , "trying to reuse chunks with size > %d, slot.n_past = %d \n " , params . n_cache_reuse , slot . n_past );
while ( head_c < slot . cache_tokens . size () &&
head_p < prompt_tokens . size ()) {
size_t n_match = 0 ;
while ( head_c + n_match < slot . cache_tokens . size () &&
head_p + n_match < prompt_tokens . size () &&
slot . cache_tokens [ head_c + n_match ] == prompt_tokens [ head_p + n_match ]) {
n_match ++ ;
}
if ( n_match >= ( size_t ) params . n_cache_reuse ) {
2024-10-15 16:28:55 +03:00
SLT_INF ( slot , "reusing chunk with size %zu, shifting KV cache [%zu, %zu) -> [%zu, %zu) \n " , n_match , head_c , head_c + n_match , head_p , head_p + n_match );
2024-10-13 18:52:48 +03:00
//for (size_t i = head_p; i < head_p + n_match; i++) {
// SLT_DBG(slot, "cache token %3zu: %6d '%s'\n", i, prompt_tokens[i], common_token_to_piece(ctx, prompt_tokens[i]).c_str());
//}
const int64_t kv_shift = ( int64_t ) head_p - ( int64_t ) head_c ;
llama_kv_cache_seq_rm ( ctx , slot . id + 1 , head_p , head_c );
llama_kv_cache_seq_add ( ctx , slot . id + 1 , head_c , - 1 , kv_shift );
for ( size_t i = 0 ; i < n_match ; i ++ ) {
slot . cache_tokens [ head_p + i ] = slot . cache_tokens [ head_c + i ];
slot . n_past ++ ;
}
head_c += n_match ;
head_p += n_match ;
} else {
head_c += 1 ;
}
}
SLT_DBG ( slot , "after context reuse, new slot.n_past = %d \n " , slot . n_past );
}
2024-03-07 11:41:53 +02:00
}
}
if ( slot . n_past == slot . n_prompt_tokens && slot . n_past > 0 ) {
// we have to evaluate at least 1 token to generate logits.
2024-09-15 20:46:12 +03:00
SLT_WRN ( slot , "need to evaluate at least 1 token to generate logits, n_past = %d, n_prompt_tokens = %d \n " , slot . n_past , slot . n_prompt_tokens );
2024-03-07 11:41:53 +02:00
slot . n_past -- ;
}
slot . n_prompt_tokens_processed = 0 ;
2023-10-22 22:53:08 +03:00
}
2024-09-28 17:42:03 +03:00
// non-causal tasks require to fit the entire prompt in the physical batch
2024-10-24 21:51:22 +02:00
if ( slot . inf_type == SERVER_TASK_INF_TYPE_EMBEDDING || slot . inf_type == SERVER_TASK_INF_TYPE_RERANK ) {
2024-03-07 11:41:53 +02:00
// cannot fit the prompt in the current batch - will try next iter
if ( batch . n_tokens + slot . n_prompt_tokens > n_batch ) {
continue ;
2024-01-27 14:38:05 +01:00
}
2023-10-22 22:53:08 +03:00
}
2024-07-12 03:14:12 -05:00
// check that we are in the right batch_type, if not defer the slot
2024-09-28 17:42:03 +03:00
const bool slot_type =
2024-10-24 21:51:22 +02:00
slot . inf_type == SERVER_TASK_INF_TYPE_EMBEDDING ||
slot . inf_type == SERVER_TASK_INF_TYPE_RERANK ? 1 : 0 ;
2024-09-28 17:42:03 +03:00
2024-07-12 03:14:12 -05:00
if ( batch_type == - 1 ) {
batch_type = slot_type ;
} else if ( batch_type != slot_type ) {
continue ;
}
2024-03-08 17:31:00 -05:00
// keep only the common part
2024-10-12 16:06:31 +03:00
if ( ! llama_kv_cache_seq_rm ( ctx , slot . id + 1 , slot . n_past , - 1 )) {
2024-03-08 17:31:00 -05:00
// could not partially delete (likely using a non-Transformer model)
llama_kv_cache_seq_rm ( ctx , slot . id + 1 , - 1 , - 1 );
2024-10-12 14:51:54 +03:00
// there is no common part left
2024-03-08 17:31:00 -05:00
slot . n_past = 0 ;
}
2024-10-12 16:06:31 +03:00
SLT_INF ( slot , "kv cache rm [%d, end) \n " , slot . n_past );
2024-03-08 17:31:00 -05:00
// remove the non-common part from the cache
slot . cache_tokens . resize ( slot . n_past );
2024-02-09 02:49:49 -08:00
2024-03-07 11:41:53 +02:00
// add prompt tokens for processing in the current batch
2024-10-12 16:06:31 +03:00
while ( slot . n_past < slot . n_prompt_tokens && batch . n_tokens < n_batch ) {
common_batch_add ( batch , prompt_tokens [ slot . n_past ], slot . n_past , { slot . id + 1 }, false );
2024-03-07 11:41:53 +02:00
if ( slot . params . cache_prompt ) {
slot . cache_tokens . push_back ( prompt_tokens [ slot . n_past ]);
}
slot . n_prompt_tokens_processed ++ ;
2024-10-12 16:06:31 +03:00
slot . n_past ++ ;
2023-10-22 22:53:08 +03:00
}
2024-09-15 20:46:12 +03:00
SLT_INF ( slot , "prompt processing progress, n_past = %d, n_tokens = %d, progress = %f \n " , slot . n_past , batch . n_tokens , ( float ) slot . n_prompt_tokens_processed / slot . n_prompt_tokens );
2023-10-22 22:53:08 +03:00
2024-09-06 23:21:29 +02:00
// entire prompt has been processed
2024-03-07 11:41:53 +02:00
if ( slot . n_past == slot . n_prompt_tokens ) {
2024-09-06 23:21:29 +02:00
slot . state = SLOT_STATE_DONE_PROMPT ;
2024-03-07 11:41:53 +02:00
GGML_ASSERT ( batch . n_tokens > 0 );
2024-10-23 13:27:51 -06:00
common_sampler_reset ( slot . smpl );
// Process all prompt tokens through sampler system
for ( int i = 0 ; i < slot . n_prompt_tokens ; ++ i ) {
common_sampler_accept ( slot . smpl , prompt_tokens [ i ], false );
}
2024-03-07 11:41:53 +02:00
// extract the logits only for the last token
2023-10-22 22:53:08 +03:00
batch . logits [ batch . n_tokens - 1 ] = true ;
2024-03-07 11:41:53 +02:00
slot . n_decoded = 0 ;
slot . i_batch = batch . n_tokens - 1 ;
2024-09-15 20:46:12 +03:00
SLT_INF ( slot , "prompt done, n_past = %d, n_tokens = %d \n " , slot . n_past , batch . n_tokens );
2024-03-07 11:41:53 +02:00
}
}
if ( batch . n_tokens >= n_batch ) {
break ;
2023-10-22 22:53:08 +03:00
}
}
}
2024-03-07 11:41:53 +02:00
if ( batch . n_tokens == 0 ) {
2024-09-15 20:46:12 +03:00
SRV_WRN ( "%s" , "no tokens to decode \n " );
2024-03-11 10:56:41 +01:00
return ;
2023-10-22 22:53:08 +03:00
}
2024-09-15 20:46:12 +03:00
SRV_DBG ( "decoding batch, n_tokens = %d \n " , batch . n_tokens );
2024-03-07 11:41:53 +02:00
2024-07-12 03:14:12 -05:00
// make sure we're in the right embedding mode
llama_set_embeddings ( ctx , batch_type == 1 );
2024-03-07 11:41:53 +02:00
// process the created batch of tokens
2024-04-26 12:15:30 +02:00
for ( int32_t i = 0 ; i < batch . n_tokens ; i += n_batch ) {
2024-03-04 22:31:20 +02:00
const int32_t n_tokens = std :: min ( n_batch , batch . n_tokens - i );
2024-01-27 14:38:05 +01:00
2024-03-07 11:41:53 +02:00
llama_batch batch_view = {
2023-10-22 22:53:08 +03:00
n_tokens ,
batch . token + i ,
nullptr ,
batch . pos + i ,
batch . n_seq_id + i ,
batch . seq_id + i ,
batch . logits + i ,
};
const int ret = llama_decode ( ctx , batch_view );
2024-09-06 23:21:29 +02:00
metrics . on_decoded ( slots );
2024-01-27 14:38:05 +01:00
2024-03-07 11:41:53 +02:00
if ( ret != 0 ) {
if ( n_batch == 1 || ret < 0 ) {
2023-10-22 22:53:08 +03:00
// if you get here, it means the KV cache is full - try increasing it via the context size
2024-09-15 20:46:12 +03:00
SRV_ERR ( "failed to decode the batch: KV cache is full - try increasing it via the context size, i = %d, n_batch = %d, ret = %d \n " , i , n_batch , ret );
2024-03-11 10:56:41 +01:00
for ( auto & slot : slots ) {
slot . release ();
send_error ( slot , "Input prompt is too big compared to KV size. Please try increasing KV size." );
}
break ; // break loop of n_batch
2023-10-22 22:53:08 +03:00
}
// retry with half the batch size to try to find a free slot in the KV cache
n_batch /= 2 ;
i -= n_batch ;
2024-03-07 11:41:53 +02:00
2024-09-15 20:46:12 +03:00
SRV_WRN ( "failed to find free space in the KV cache, retrying with smaller batch size - try increasing it via the context size or enable defragmentation, i = %d, n_batch = %d, ret = %d \n " , i , n_batch , ret );
2024-04-12 13:49:21 +02:00
2024-03-11 10:56:41 +01:00
continue ; // continue loop of n_batch
2023-10-22 22:53:08 +03:00
}
2024-03-07 11:41:53 +02:00
for ( auto & slot : slots ) {
2024-09-06 23:21:29 +02:00
if ( slot . i_batch < ( int ) i || slot . i_batch >= ( int ) ( i + n_tokens )) {
2024-03-11 10:56:41 +01:00
continue ; // continue loop of slots
2023-10-22 22:53:08 +03:00
}
2024-09-06 23:21:29 +02:00
if ( slot . state == SLOT_STATE_DONE_PROMPT ) {
2024-10-24 21:51:22 +02:00
if ( slot . inf_type == SERVER_TASK_INF_TYPE_EMBEDDING ) {
2024-09-06 23:21:29 +02:00
// prompt evaluated for embedding
send_embedding ( slot , batch_view );
slot . release ();
slot . i_batch = - 1 ;
continue ; // continue loop of slots
}
2024-09-07 15:16:19 +03:00
2024-10-24 21:51:22 +02:00
if ( slot . inf_type == SERVER_TASK_INF_TYPE_RERANK ) {
2024-09-28 17:42:03 +03:00
send_rerank ( slot , batch_view );
slot . release ();
slot . i_batch = - 1 ;
continue ; // continue loop of slots
}
2024-09-07 15:16:19 +03:00
// prompt evaluated for next-token prediction
slot . state = SLOT_STATE_GENERATING ;
2024-09-06 23:21:29 +02:00
} else if ( slot . state != SLOT_STATE_GENERATING ) {
2024-03-11 10:56:41 +01:00
continue ; // continue loop of slots
2023-10-22 22:53:08 +03:00
}
completion_token_output result ;
2024-10-10 22:57:42 +02:00
const llama_token id = common_sampler_sample ( slot . smpl , ctx , slot . i_batch - i );
2023-10-22 22:53:08 +03:00
2024-10-10 22:57:42 +02:00
common_sampler_accept ( slot . smpl , id , true );
2023-10-22 22:53:08 +03:00
2024-01-07 08:45:26 +02:00
slot . n_decoded += 1 ;
2024-03-07 11:41:53 +02:00
if ( slot . n_decoded == 1 ) {
slot . t_start_generation = ggml_time_us ();
slot . t_prompt_processing = ( slot . t_start_generation - slot . t_start_process_prompt ) / 1e3 ;
2024-02-25 13:49:43 +01:00
metrics . on_prompt_eval ( slot );
2023-10-22 22:53:08 +03:00
}
result . tok = id ;
2024-10-10 22:57:42 +02:00
const auto * cur_p = common_sampler_get_candidates ( slot . smpl );
2023-10-22 22:53:08 +03:00
2024-09-07 15:16:19 +03:00
for ( size_t i = 0 ; i < ( size_t ) slot . sparams . n_probs ; ++ i ) {
result . probs . push_back ({
cur_p -> data [ i ]. id ,
i >= cur_p -> size ? 0.0f : cur_p -> data [ i ]. p ,
});
2023-10-22 22:53:08 +03:00
}
2024-03-07 11:41:53 +02:00
if ( ! process_token ( result , slot )) {
2024-09-06 23:21:29 +02:00
// release slot because of stop condition
2023-10-22 22:53:08 +03:00
slot . release ();
slot . print_timings ();
2023-10-24 23:08:20 +03:00
send_final_response ( slot );
2024-02-25 13:49:43 +01:00
metrics . on_prediction ( slot );
2023-10-22 22:53:08 +03:00
}
slot . i_batch = - 1 ;
}
}
2024-02-25 13:50:32 +01:00
2024-09-15 20:46:12 +03:00
SRV_DBG ( "%s" , "run slots completed \n " );
2023-06-20 01:12:39 +03:00
}
2024-03-02 22:00:14 +01:00
2024-03-07 11:41:53 +02:00
json model_meta () const {
return json {
{ "vocab_type" , llama_vocab_type ( model )},
{ "n_vocab" , llama_n_vocab ( model )},
{ "n_ctx_train" , llama_n_ctx_train ( model )},
{ "n_embd" , llama_n_embd ( model )},
{ "n_params" , llama_model_n_params ( model )},
{ "size" , llama_model_size ( model )},
2024-03-02 22:00:14 +01:00
};
}
2023-06-17 07:53:04 -04:00
};
2024-03-07 11:41:53 +02:00
static void log_server_request ( const httplib :: Request & req , const httplib :: Response & res ) {
2024-02-25 13:50:32 +01:00
// skip GH copilot requests when using default port
2024-03-07 11:41:53 +02:00
if ( req . path == "/v1/health" || req . path == "/v1/completions" ) {
2024-02-25 13:50:32 +01:00
return ;
}
2024-09-15 20:46:12 +03:00
LOG_INF ( "request: %s %s %s %d \n " , req . method . c_str (), req . path . c_str (), req . remote_addr . c_str (), res . status );
2023-07-04 10:05:27 -04:00
2024-09-15 20:46:12 +03:00
LOG_DBG ( "request: %s \n " , req . body . c_str ());
LOG_DBG ( "response: %s \n " , res . body . c_str ());
2023-06-17 07:53:04 -04:00
}
2024-02-18 08:23:16 -08:00
std :: function < void ( int ) > shutdown_handler ;
2024-02-28 09:55:37 +01:00
std :: atomic_flag is_terminating = ATOMIC_FLAG_INIT ;
2024-03-07 11:41:53 +02:00
2024-02-28 09:55:37 +01:00
inline void signal_handler ( int signal ) {
if ( is_terminating . test_and_set ()) {
// in case it hangs, we can force terminate the server by hitting Ctrl+C twice
// this is for better developer experience, we can remove when the server is stable enough
fprintf ( stderr , "Received second interrupt, terminating immediately. \n " );
exit ( 1 );
}
2024-03-07 11:41:53 +02:00
2024-02-28 09:55:37 +01:00
shutdown_handler ( signal );
}
2024-02-18 08:23:16 -08:00
2024-03-07 11:41:53 +02:00
int main ( int argc , char ** argv ) {
2023-06-17 07:53:04 -04:00
// own arguments required by this example
2024-10-10 22:57:42 +02:00
common_params params ;
2024-06-04 21:23:39 +03:00
2024-10-10 22:57:42 +02:00
if ( ! common_params_parse ( argc , argv , params , LLAMA_EXAMPLE_SERVER )) {
2024-06-04 21:23:39 +03:00
return 1 ;
}
2024-10-10 22:57:42 +02:00
common_init ();
2024-09-15 20:46:12 +03:00
// enabling this will output extra debug information in the HTTP responses from the server
// see format_final_response_oaicompat()
const bool verbose = params . verbosity > 9 ;
2023-06-17 07:53:04 -04:00
// struct that contains llama context and inference
2024-03-07 11:41:53 +02:00
server_context ctx_server ;
2023-06-17 07:53:04 -04:00
2024-03-07 11:41:53 +02:00
if ( params . model_alias == "unknown" ) {
2023-06-17 07:53:04 -04:00
params . model_alias = params . model ;
}
2024-02-16 01:31:07 -08:00
llama_backend_init ();
llama_numa_init ( params . numa );
2023-06-17 07:53:04 -04:00
2024-09-15 20:46:12 +03:00
LOG_INF ( "system info: n_threads = %d, n_threads_batch = %d, total_threads = %d \n " , params . cpuparams . n_threads , params . cpuparams_batch . n_threads , std :: thread :: hardware_concurrency ());
LOG_INF ( " \n " );
2024-10-10 22:57:42 +02:00
LOG_INF ( "%s \n " , common_params_get_system_info ( params ). c_str ());
2024-09-15 20:46:12 +03:00
LOG_INF ( " \n " );
2023-06-17 07:53:04 -04:00
2024-03-09 02:57:09 -07:00
std :: unique_ptr < httplib :: Server > svr ;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
2024-06-04 21:23:39 +03:00
if ( params . ssl_file_key != "" && params . ssl_file_cert != "" ) {
2024-09-18 14:28:20 +08:00
LOG_INF ( "Running with SSL: key = %s, cert = %s \n " , params . ssl_file_key . c_str (), params . ssl_file_cert . c_str ());
2024-03-09 02:57:09 -07:00
svr . reset (
2024-06-04 21:23:39 +03:00
new httplib :: SSLServer ( params . ssl_file_cert . c_str (), params . ssl_file_key . c_str ())
2024-03-09 02:57:09 -07:00
);
} else {
2024-09-18 14:28:20 +08:00
LOG_INF ( "Running without SSL \n " );
2024-03-09 02:57:09 -07:00
svr . reset ( new httplib :: Server ());
}
#else
2024-09-25 14:05:13 +02:00
if ( params . ssl_file_key != "" && params . ssl_file_cert != "" ) {
LOG_ERR ( "Server is built without SSL support \n " );
return 1 ;
}
2024-03-09 02:57:09 -07:00
svr . reset ( new httplib :: Server ());
#endif
2024-01-10 14:56:05 -05:00
2024-01-11 09:10:34 +02:00
std :: atomic < server_state > state { SERVER_STATE_LOADING_MODEL };
2024-01-10 14:56:05 -05:00
2024-03-09 02:57:09 -07:00
svr -> set_default_headers ({{ "Server" , "llama.cpp" }});
2024-01-11 19:02:48 +01:00
// CORS preflight
2024-08-16 17:19:05 +02:00
svr -> Options ( R "(.*)" , []( const httplib :: Request & , httplib :: Response & res ) {
// Access-Control-Allow-Origin is already set by middleware
2024-01-11 19:02:48 +01:00
res . set_header ( "Access-Control-Allow-Credentials" , "true" );
2024-03-07 11:41:53 +02:00
res . set_header ( "Access-Control-Allow-Methods" , "POST" );
res . set_header ( "Access-Control-Allow-Headers" , "*" );
2024-08-16 17:19:05 +02:00
return res . set_content ( "" , "text/html" ); // blank response, no data
2024-01-11 19:02:48 +01:00
});
2024-01-10 14:56:05 -05:00
2024-03-09 11:27:53 +01:00
svr -> set_logger ( log_server_request );
2024-09-15 20:46:12 +03:00
auto res_error = []( httplib :: Response & res , const json & error_data ) {
2024-03-11 10:56:41 +01:00
json final_response {{ "error" , error_data }};
2024-08-27 18:28:06 +08:00
res . set_content ( final_response . dump ( - 1 , ' ' , false , json :: error_handler_t :: replace ), MIMETYPE_JSON );
2024-03-11 10:56:41 +01:00
res . status = json_value ( error_data , "code" , 500 );
};
2024-03-09 11:27:53 +01:00
2024-09-15 20:46:12 +03:00
auto res_ok = []( httplib :: Response & res , const json & data ) {
2024-09-02 17:11:51 +02:00
res . set_content ( data . dump ( - 1 , ' ' , false , json :: error_handler_t :: replace ), MIMETYPE_JSON );
res . status = 200 ;
};
2024-03-11 10:56:41 +01:00
svr -> set_exception_handler ([ & res_error ]( const httplib :: Request & , httplib :: Response & res , std :: exception_ptr ep ) {
std :: string message ;
2024-03-09 11:27:53 +01:00
try {
2024-09-15 20:46:12 +03:00
std :: rethrow_exception ( ep );
2024-03-11 10:56:41 +01:00
} catch ( std :: exception & e ) {
message = e . what ();
2024-03-09 11:27:53 +01:00
} catch (...) {
2024-03-11 10:56:41 +01:00
message = "Unknown Exception" ;
2024-03-09 11:27:53 +01:00
}
2024-03-11 10:56:41 +01:00
json formatted_error = format_error_response ( message , ERROR_TYPE_SERVER );
2024-09-15 20:46:12 +03:00
LOG_WRN ( "got exception: %s \n " , formatted_error . dump (). c_str ());
2024-03-11 10:56:41 +01:00
res_error ( res , formatted_error );
2024-03-09 11:27:53 +01:00
});
2024-03-11 10:56:41 +01:00
svr -> set_error_handler ([ & res_error ]( const httplib :: Request & , httplib :: Response & res ) {
2024-03-09 11:27:53 +01:00
if ( res . status == 404 ) {
2024-03-11 10:56:41 +01:00
res_error ( res , format_error_response ( "File Not Found" , ERROR_TYPE_NOT_FOUND ));
2024-03-09 11:27:53 +01:00
}
2024-03-11 10:56:41 +01:00
// for other error codes, we skip processing here because it's already done by res_error()
2024-03-09 11:27:53 +01:00
});
// set timeouts and change hostname and port
2024-06-04 21:23:39 +03:00
svr -> set_read_timeout ( params . timeout_read );
svr -> set_write_timeout ( params . timeout_write );
2024-03-09 11:27:53 +01:00
std :: unordered_map < std :: string , std :: string > log_data ;
2024-06-04 21:23:39 +03:00
log_data [ "hostname" ] = params . hostname ;
log_data [ "port" ] = std :: to_string ( params . port );
2024-03-09 11:27:53 +01:00
2024-06-04 21:23:39 +03:00
if ( params . api_keys . size () == 1 ) {
auto key = params . api_keys [ 0 ];
2024-03-09 11:27:53 +01:00
log_data [ "api_key" ] = "api_key: ****" + key . substr ( std :: max (( int )( key . length () - 4 ), 0 ));
2024-06-04 21:23:39 +03:00
} else if ( params . api_keys . size () > 1 ) {
log_data [ "api_key" ] = "api_key: " + std :: to_string ( params . api_keys . size ()) + " keys loaded" ;
2024-03-09 11:27:53 +01:00
}
2024-06-08 07:50:31 +00:00
// Necessary similarity of prompt for slot selection
ctx_server . slot_prompt_similarity = params . slot_prompt_similarity ;
2024-03-09 11:27:53 +01:00
//
// Middlewares
//
2024-06-04 21:23:39 +03:00
auto middleware_validate_api_key = [ & params , & res_error ]( const httplib :: Request & req , httplib :: Response & res ) {
2024-10-08 13:27:04 +02:00
static const std :: unordered_set < std :: string > public_endpoints = {
"/health" ,
"/models" ,
"/v1/models" ,
2024-03-09 11:27:53 +01:00
};
// If API key is not set, skip validation
2024-06-04 21:23:39 +03:00
if ( params . api_keys . empty ()) {
2024-03-09 11:27:53 +01:00
return true ;
}
2024-10-08 13:27:04 +02:00
// If path is public, skip validation
if ( public_endpoints . find ( req . path ) != public_endpoints . end ()) {
2024-03-09 11:27:53 +01:00
return true ;
}
// Check for API key in the header
auto auth_header = req . get_header_value ( "Authorization" );
std :: string prefix = "Bearer " ;
if ( auth_header . substr ( 0 , prefix . size ()) == prefix ) {
std :: string received_api_key = auth_header . substr ( prefix . size ());
2024-06-04 21:23:39 +03:00
if ( std :: find ( params . api_keys . begin (), params . api_keys . end (), received_api_key ) != params . api_keys . end ()) {
2024-03-09 11:27:53 +01:00
return true ; // API key is valid
}
}
// API key is invalid or not provided
2024-03-11 10:56:41 +01:00
res_error ( res , format_error_response ( "Invalid API Key" , ERROR_TYPE_AUTHENTICATION ));
2024-03-09 11:27:53 +01:00
2024-09-15 20:46:12 +03:00
LOG_WRN ( "Unauthorized: Invalid API Key \n " );
2024-03-09 11:27:53 +01:00
return false ;
};
2024-09-13 14:23:11 +02:00
auto middleware_server_state = [ & res_error , & state ]( const httplib :: Request & req , httplib :: Response & res ) {
2024-08-16 17:19:05 +02:00
server_state current_state = state . load ();
if ( current_state == SERVER_STATE_LOADING_MODEL ) {
2024-10-25 17:57:54 +02:00
auto tmp = string_split < std :: string > ( req . path , '.' );
2024-09-13 14:23:11 +02:00
if ( req . path == "/" || tmp . back () == "html" ) {
res . set_content ( reinterpret_cast < const char *> ( loading_html ), loading_html_len , "text/html; charset=utf-8" );
res . status = 503 ;
} else {
res_error ( res , format_error_response ( "Loading model" , ERROR_TYPE_UNAVAILABLE ));
}
2024-08-16 17:19:05 +02:00
return false ;
}
return true ;
};
2024-03-09 11:27:53 +01:00
// register server middlewares
2024-08-16 17:19:05 +02:00
svr -> set_pre_routing_handler ([ & middleware_validate_api_key , & middleware_server_state ]( const httplib :: Request & req , httplib :: Response & res ) {
res . set_header ( "Access-Control-Allow-Origin" , req . get_header_value ( "Origin" ));
if ( ! middleware_server_state ( req , res )) {
return httplib :: Server :: HandlerResponse :: Handled ;
}
2024-03-09 11:27:53 +01:00
if ( ! middleware_validate_api_key ( req , res )) {
return httplib :: Server :: HandlerResponse :: Handled ;
}
return httplib :: Server :: HandlerResponse :: Unhandled ;
});
//
// Route handlers (or controllers)
//
2024-08-16 17:19:05 +02:00
const auto handle_health = [ & ]( const httplib :: Request & , httplib :: Response & res ) {
// error and loading states are handled by middleware
json health = {{ "status" , "ok" }};
2024-09-02 17:11:51 +02:00
res_ok ( res , health );
2023-12-15 13:49:01 +02:00
};
2024-08-16 17:19:05 +02:00
const auto handle_slots = [ & ]( const httplib :: Request & req , httplib :: Response & res ) {
2024-06-04 21:23:39 +03:00
if ( ! params . endpoint_slots ) {
2024-10-08 13:27:04 +02:00
res_error ( res , format_error_response ( "This server does not support slots endpoint. Start it with `--slots`" , ERROR_TYPE_NOT_SUPPORTED ));
2024-03-09 11:27:53 +01:00
return ;
}
2023-06-17 07:53:04 -04:00
2024-03-09 11:27:53 +01:00
// request slots data using task queue
server_task task ;
task . id = ctx_server . queue_tasks . get_new_id ();
task . type = SERVER_TASK_TYPE_METRICS ;
2023-07-04 10:05:27 -04:00
2024-03-09 11:27:53 +01:00
ctx_server . queue_results . add_waiting_task_id ( task . id );
2024-09-06 23:21:29 +02:00
ctx_server . queue_tasks . post ( task , true ); // high-priority task
2023-06-17 07:53:04 -04:00
2024-03-09 11:27:53 +01:00
// get the result
server_task_result result = ctx_server . queue_results . recv ( task . id );
ctx_server . queue_results . remove_waiting_task_id ( task . id );
2023-08-15 06:14:14 +08:00
2024-08-16 17:19:05 +02:00
// optionally return "fail_on_no_slot" error
const int n_idle_slots = result . data . at ( "idle" );
if ( req . has_param ( "fail_on_no_slot" )) {
if ( n_idle_slots == 0 ) {
res_error ( res , format_error_response ( "no slot available" , ERROR_TYPE_UNAVAILABLE ));
return ;
}
}
2024-09-02 17:11:51 +02:00
res_ok ( res , result . data . at ( "slots" ));
2024-03-09 11:27:53 +01:00
};
const auto handle_metrics = [ & ]( const httplib :: Request & , httplib :: Response & res ) {
2024-06-04 21:23:39 +03:00
if ( ! params . endpoint_metrics ) {
2024-08-16 17:19:05 +02:00
res_error ( res , format_error_response ( "This server does not support metrics endpoint. Start it with `--metrics`" , ERROR_TYPE_NOT_SUPPORTED ));
2024-03-09 11:27:53 +01:00
return ;
}
// request slots data using task queue
server_task task ;
task . id = ctx_server . queue_tasks . get_new_id ();
task . id_target = - 1 ;
task . type = SERVER_TASK_TYPE_METRICS ;
task . data . push_back ({{ "reset_bucket" , true }});
ctx_server . queue_results . add_waiting_task_id ( task . id );
2024-09-06 23:21:29 +02:00
ctx_server . queue_tasks . post ( task , true ); // high-priority task
2024-03-09 11:27:53 +01:00
// get the result
server_task_result result = ctx_server . queue_results . recv ( task . id );
ctx_server . queue_results . remove_waiting_task_id ( task . id );
json data = result . data ;
2024-05-08 21:53:08 +02:00
const uint64_t n_prompt_tokens_processed = data . at ( "n_prompt_tokens_processed" );
const uint64_t t_prompt_processing = data . at ( "t_prompt_processing" );
2024-03-09 11:27:53 +01:00
2024-05-08 21:53:08 +02:00
const uint64_t n_tokens_predicted = data . at ( "n_tokens_predicted" );
const uint64_t t_tokens_generation = data . at ( "t_tokens_generation" );
2024-03-09 11:27:53 +01:00
2024-09-06 23:21:29 +02:00
const uint64_t n_decode_total = data . at ( "n_decode_total" );
const uint64_t n_busy_slots_total = data . at ( "n_busy_slots_total" );
2024-05-08 21:53:08 +02:00
const int32_t kv_cache_used_cells = data . at ( "kv_cache_used_cells" );
2024-03-09 11:27:53 +01:00
// metrics definition: https://prometheus.io/docs/practices/naming/#metric-names
json all_metrics_def = json {
{ "counter" , {{
{ "name" , "prompt_tokens_total" },
{ "help" , "Number of prompt tokens processed." },
2024-05-08 21:53:08 +02:00
{ "value" , ( uint64_t ) data . at ( "n_prompt_tokens_processed_total" )}
2024-03-09 11:27:53 +01:00
}, {
{ "name" , "prompt_seconds_total" },
{ "help" , "Prompt process time" },
2024-05-08 21:53:08 +02:00
{ "value" , ( uint64_t ) data . at ( "t_prompt_processing_total" ) / 1.e3 }
2024-03-09 11:27:53 +01:00
}, {
{ "name" , "tokens_predicted_total" },
{ "help" , "Number of generation tokens processed." },
2024-05-08 21:53:08 +02:00
{ "value" , ( uint64_t ) data . at ( "n_tokens_predicted_total" )}
2024-03-09 11:27:53 +01:00
}, {
{ "name" , "tokens_predicted_seconds_total" },
{ "help" , "Predict process time" },
2024-05-08 21:53:08 +02:00
{ "value" , ( uint64_t ) data . at ( "t_tokens_generation_total" ) / 1.e3 }
2024-09-06 23:21:29 +02:00
}, {
{ "name" , "n_decode_total" },
{ "help" , "Total number of llama_decode() calls" },
{ "value" , n_decode_total }
}, {
{ "name" , "n_busy_slots_per_decode" },
{ "help" , "Average number of busy slots per llama_decode() call" },
{ "value" , ( float ) n_busy_slots_total / ( float ) n_decode_total }
2024-03-09 11:27:53 +01:00
}}},
{ "gauge" , {{
{ "name" , "prompt_tokens_seconds" },
{ "help" , "Average prompt throughput in tokens/s." },
{ "value" , n_prompt_tokens_processed ? 1.e3 / t_prompt_processing * n_prompt_tokens_processed : 0. }
},{
{ "name" , "predicted_tokens_seconds" },
{ "help" , "Average generation throughput in tokens/s." },
{ "value" , n_tokens_predicted ? 1.e3 / t_tokens_generation * n_tokens_predicted : 0. }
},{
{ "name" , "kv_cache_usage_ratio" },
{ "help" , "KV-cache usage. 1 means 100 percent usage." },
{ "value" , 1. * kv_cache_used_cells / params . n_ctx }
},{
{ "name" , "kv_cache_tokens" },
{ "help" , "KV-cache tokens." },
2024-05-08 21:53:08 +02:00
{ "value" , ( uint64_t ) data . at ( "kv_cache_tokens_count" )}
2024-03-09 11:27:53 +01:00
},{
{ "name" , "requests_processing" },
{ "help" , "Number of request processing." },
2024-05-08 21:53:08 +02:00
{ "value" , ( uint64_t ) data . at ( "processing" )}
2024-03-09 11:27:53 +01:00
},{
{ "name" , "requests_deferred" },
{ "help" , "Number of request deferred." },
2024-05-08 21:53:08 +02:00
{ "value" , ( uint64_t ) data . at ( "deferred" )}
2024-03-09 11:27:53 +01:00
}}}
};
std :: stringstream prometheus ;
for ( const auto & el : all_metrics_def . items ()) {
const auto & type = el . key ();
const auto & metrics_def = el . value ();
for ( const auto & metric_def : metrics_def ) {
2024-05-08 21:53:08 +02:00
const std :: string name = metric_def . at ( "name" );
const std :: string help = metric_def . at ( "help" );
2024-03-09 11:27:53 +01:00
auto value = json_value ( metric_def , "value" , 0. );
prometheus << "# HELP llamacpp:" << name << " " << help << " \n "
<< "# TYPE llamacpp:" << name << " " << type << " \n "
<< "llamacpp:" << name << " " << value << " \n " ;
}
}
2024-05-08 21:53:08 +02:00
const int64_t t_start = data . at ( "t_start" );
2024-03-09 11:27:53 +01:00
res . set_header ( "Process-Start-Time-Unix" , std :: to_string ( t_start ));
res . set_content ( prometheus . str (), "text/plain; version=0.0.4" );
res . status = 200 ; // HTTP OK
};
2024-09-02 17:11:51 +02:00
const auto handle_slots_save = [ & ctx_server , & res_error , & res_ok , & params ]( const httplib :: Request & req , httplib :: Response & res , int id_slot ) {
2024-04-08 20:43:30 +08:00
json request_data = json :: parse ( req . body );
2024-05-08 21:53:08 +02:00
std :: string filename = request_data . at ( "filename" );
2024-05-22 20:04:20 +03:00
if ( ! fs_validate_filename ( filename )) {
2024-04-08 20:43:30 +08:00
res_error ( res , format_error_response ( "Invalid filename" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
2024-06-04 21:23:39 +03:00
std :: string filepath = params . slot_save_path + filename ;
2024-04-08 20:43:30 +08:00
server_task task ;
task . type = SERVER_TASK_TYPE_SLOT_SAVE ;
task . data = {
{ "id_slot" , id_slot },
{ "filename" , filename },
2024-09-06 23:21:29 +02:00
{ "filepath" , filepath },
2024-04-08 20:43:30 +08:00
};
const int id_task = ctx_server . queue_tasks . post ( task );
ctx_server . queue_results . add_waiting_task_id ( id_task );
server_task_result result = ctx_server . queue_results . recv ( id_task );
ctx_server . queue_results . remove_waiting_task_id ( id_task );
if ( result . error ) {
res_error ( res , result . data );
} else {
2024-09-02 17:11:51 +02:00
res_ok ( res , result . data );
2024-04-08 20:43:30 +08:00
}
};
2024-09-02 17:11:51 +02:00
const auto handle_slots_restore = [ & ctx_server , & res_error , & res_ok , & params ]( const httplib :: Request & req , httplib :: Response & res , int id_slot ) {
2024-04-08 20:43:30 +08:00
json request_data = json :: parse ( req . body );
2024-05-08 21:53:08 +02:00
std :: string filename = request_data . at ( "filename" );
2024-05-22 20:04:20 +03:00
if ( ! fs_validate_filename ( filename )) {
2024-04-08 20:43:30 +08:00
res_error ( res , format_error_response ( "Invalid filename" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
2024-06-04 21:23:39 +03:00
std :: string filepath = params . slot_save_path + filename ;
2024-04-08 20:43:30 +08:00
server_task task ;
task . type = SERVER_TASK_TYPE_SLOT_RESTORE ;
task . data = {
{ "id_slot" , id_slot },
{ "filename" , filename },
2024-09-06 23:21:29 +02:00
{ "filepath" , filepath },
2024-04-08 20:43:30 +08:00
};
const int id_task = ctx_server . queue_tasks . post ( task );
ctx_server . queue_results . add_waiting_task_id ( id_task );
server_task_result result = ctx_server . queue_results . recv ( id_task );
ctx_server . queue_results . remove_waiting_task_id ( id_task );
if ( result . error ) {
res_error ( res , result . data );
} else {
2024-09-02 17:11:51 +02:00
res_ok ( res , result . data );
2024-04-08 20:43:30 +08:00
}
};
2024-09-02 17:11:51 +02:00
const auto handle_slots_erase = [ & ctx_server , & res_error , & res_ok ]( const httplib :: Request & /* req */ , httplib :: Response & res , int id_slot ) {
2024-04-08 20:43:30 +08:00
server_task task ;
task . type = SERVER_TASK_TYPE_SLOT_ERASE ;
task . data = {
{ "id_slot" , id_slot },
};
const int id_task = ctx_server . queue_tasks . post ( task );
ctx_server . queue_results . add_waiting_task_id ( id_task );
server_task_result result = ctx_server . queue_results . recv ( id_task );
ctx_server . queue_results . remove_waiting_task_id ( id_task );
if ( result . error ) {
res_error ( res , result . data );
} else {
2024-09-02 17:11:51 +02:00
res_ok ( res , result . data );
2024-04-08 20:43:30 +08:00
}
};
2024-09-02 17:11:51 +02:00
const auto handle_slots_action = [ & params , & res_error , & handle_slots_save , & handle_slots_restore , & handle_slots_erase ]( const httplib :: Request & req , httplib :: Response & res ) {
if ( params . slot_save_path . empty ()) {
res_error ( res , format_error_response ( "This server does not support slots action. Start it with `--slot-save-path`" , ERROR_TYPE_NOT_SUPPORTED ));
return ;
}
2024-04-08 20:43:30 +08:00
std :: string id_slot_str = req . path_params . at ( "id_slot" );
int id_slot ;
try {
id_slot = std :: stoi ( id_slot_str );
} catch ( const std :: exception & ) {
res_error ( res , format_error_response ( "Invalid slot ID" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
std :: string action = req . get_param_value ( "action" );
if ( action == "save" ) {
handle_slots_save ( req , res , id_slot );
} else if ( action == "restore" ) {
handle_slots_restore ( req , res , id_slot );
} else if ( action == "erase" ) {
handle_slots_erase ( req , res , id_slot );
} else {
res_error ( res , format_error_response ( "Invalid action" , ERROR_TYPE_INVALID_REQUEST ));
}
};
2024-09-02 17:11:51 +02:00
const auto handle_props = [ & ctx_server , & res_ok ]( const httplib :: Request & , httplib :: Response & res ) {
2024-03-07 11:41:53 +02:00
json data = {
{ "default_generation_settings" , ctx_server . default_generation_settings_for_props },
2024-07-07 11:10:38 +02:00
{ "total_slots" , ctx_server . params . n_parallel },
2024-10-08 13:27:04 +02:00
{ "chat_template" , llama_get_chat_template ( ctx_server . model ) },
2024-03-07 11:41:53 +02:00
};
2023-07-04 10:05:27 -04:00
2024-09-02 17:11:51 +02:00
res_ok ( res , data );
2024-03-09 11:27:53 +01:00
};
2024-01-26 13:42:20 +01:00
2024-10-08 13:27:04 +02:00
const auto handle_props_change = [ & ctx_server , & res_error , & res_ok ]( const httplib :: Request & req , httplib :: Response & res ) {
if ( ! ctx_server . params . endpoint_props ) {
res_error ( res , format_error_response ( "This server does not support changing global properties. Start it with `--props`" , ERROR_TYPE_NOT_SUPPORTED ));
return ;
}
json data = json :: parse ( req . body );
2024-10-12 14:51:54 +03:00
// update any props here
2024-10-08 13:27:04 +02:00
res_ok ( res , {{ "success" , true }});
};
2024-10-24 21:51:22 +02:00
const auto handle_completions_generic = [ & ctx_server , & res_error , & res_ok ]( server_task_inf_type inf_type , json & data , httplib :: Response & res ) {
2024-09-28 17:42:03 +03:00
if ( ctx_server . params . embedding || ctx_server . params . reranking ) {
res_error ( res , format_error_response ( "This server does not support completions. Start it without `--embeddings` or `--reranking`" , ERROR_TYPE_NOT_SUPPORTED ));
2024-07-12 03:14:12 -05:00
return ;
}
2024-10-24 21:51:22 +02:00
std :: vector < server_task > tasks = ctx_server . create_tasks_inference ( data , inf_type );
2024-09-02 17:11:51 +02:00
ctx_server . queue_results . add_waiting_tasks ( tasks );
ctx_server . queue_tasks . post ( tasks );
2024-03-07 11:41:53 +02:00
2024-09-02 17:11:51 +02:00
bool stream = json_value ( data , "stream" , false );
const auto task_ids = server_task :: get_list_id ( tasks );
2024-03-07 11:41:53 +02:00
2024-09-02 17:11:51 +02:00
if ( ! stream ) {
ctx_server . receive_cmpl_results ( task_ids , [ & ]( std :: vector < server_task_result > & results ) {
if ( results . size () == 1 ) {
// single result
res_ok ( res , results [ 0 ]. data );
} else {
// multiple results (multitask)
json arr = json :: array ();
for ( const auto & res : results ) {
arr . push_back ( res . data );
2024-03-07 11:41:53 +02:00
}
2024-09-02 17:11:51 +02:00
res_ok ( res , arr );
2024-03-07 11:41:53 +02:00
}
2024-09-15 20:46:12 +03:00
}, [ & ]( const json & error_data ) {
2024-09-02 17:11:51 +02:00
res_error ( res , error_data );
});
2024-09-19 12:44:53 +03:00
ctx_server . queue_results . remove_waiting_task_ids ( task_ids );
2024-09-02 17:11:51 +02:00
} else {
const auto chunked_content_provider = [ task_ids , & ctx_server ]( size_t , httplib :: DataSink & sink ) {
2024-09-15 20:46:12 +03:00
ctx_server . receive_cmpl_results_stream ( task_ids , [ & ]( const server_task_result & result ) -> bool {
2024-09-02 17:11:51 +02:00
return server_sent_event ( sink , "data" , result . data );
2024-09-15 20:46:12 +03:00
}, [ & ]( const json & error_data ) {
2024-09-02 17:11:51 +02:00
server_sent_event ( sink , "error" , error_data );
});
2024-03-07 11:41:53 +02:00
sink . done ();
2024-09-02 17:11:51 +02:00
return false ;
};
2024-09-19 12:44:53 +03:00
auto on_complete = [ task_ids , & ctx_server ] ( bool ) {
ctx_server . queue_results . remove_waiting_task_ids ( task_ids );
};
res . set_chunked_content_provider ( "text/event-stream" , chunked_content_provider , on_complete );
2024-09-02 17:11:51 +02:00
}
};
2024-03-07 11:41:53 +02:00
2024-09-02 17:11:51 +02:00
const auto handle_completions = [ & handle_completions_generic ]( const httplib :: Request & req , httplib :: Response & res ) {
json data = json :: parse ( req . body );
2024-10-24 21:51:22 +02:00
return handle_completions_generic ( SERVER_TASK_INF_TYPE_COMPLETION , data , res );
2024-09-02 17:11:51 +02:00
};
2024-10-12 08:21:51 +03:00
const auto handle_infill = [ & ctx_server , & res_error , & handle_completions_generic ]( const httplib :: Request & req , httplib :: Response & res ) {
2024-10-24 21:51:22 +02:00
// check model compatibility
2024-10-12 08:21:51 +03:00
std :: string err ;
if ( llama_token_fim_pre ( ctx_server . model ) == LLAMA_TOKEN_NULL ) {
err += "prefix token is missing. " ;
}
if ( llama_token_fim_suf ( ctx_server . model ) == LLAMA_TOKEN_NULL ) {
err += "suffix token is missing. " ;
}
if ( llama_token_fim_mid ( ctx_server . model ) == LLAMA_TOKEN_NULL ) {
err += "middle token is missing. " ;
}
if ( ! err . empty ()) {
res_error ( res , format_error_response ( string_format ( "Infill is not supported by this model: %s" , err . c_str ()), ERROR_TYPE_NOT_SUPPORTED ));
return ;
}
2024-09-02 17:11:51 +02:00
json data = json :: parse ( req . body );
2024-10-24 21:51:22 +02:00
// validate input
if ( ! data . contains ( "input_prefix" )) {
res_error ( res , format_error_response ( " \" input_prefix \" is required" , ERROR_TYPE_INVALID_REQUEST ));
}
if ( ! data . contains ( "input_suffix" )) {
res_error ( res , format_error_response ( " \" input_suffix \" is required" , ERROR_TYPE_INVALID_REQUEST ));
}
if ( data . contains ( "input_extra" ) && ! data . at ( "input_extra" ). is_array ()) {
res_error ( res , format_error_response ( " \" input_extra \" must be an array of { \" filename \" : string, \" text \" : string}" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
json input_extra = json_value ( data , "input_extra" , json :: array ());
for ( const auto & chunk : input_extra ) {
// { "text": string, "filename": string }
if ( ! chunk . contains ( "text" ) || ! chunk . at ( "text" ). is_string ()) {
res_error ( res , format_error_response ( "extra_context chunk must contain a \" text \" field with a string value" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
// filename is optional
if ( chunk . contains ( "filename" ) && ! chunk . at ( "filename" ). is_string ()) {
res_error ( res , format_error_response ( "extra_context chunk's \" filename \" field must be a string" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
}
data [ "input_extra" ] = input_extra ; // default to empty array if it's not exist
return handle_completions_generic ( SERVER_TASK_INF_TYPE_INFILL , data , res );
2024-09-02 17:11:51 +02:00
};
// TODO: maybe merge this function with "handle_completions_generic"
2024-09-15 20:46:12 +03:00
const auto handle_chat_completions = [ & ctx_server , & params , & res_error , & res_ok , verbose ]( const httplib :: Request & req , httplib :: Response & res ) {
2024-09-28 17:42:03 +03:00
if ( ctx_server . params . embedding || ctx_server . params . reranking ) {
res_error ( res , format_error_response ( "This server does not support completions. Start it without `--embeddings` or `--reranking`" , ERROR_TYPE_NOT_SUPPORTED ));
2024-09-02 17:11:51 +02:00
return ;
}
json data = oaicompat_completion_params_parse ( ctx_server . model , json :: parse ( req . body ), params . chat_template );
2024-10-24 21:51:22 +02:00
std :: vector < server_task > tasks = ctx_server . create_tasks_inference ( data , SERVER_TASK_INF_TYPE_COMPLETION );
2024-09-02 17:11:51 +02:00
ctx_server . queue_results . add_waiting_tasks ( tasks );
ctx_server . queue_tasks . post ( tasks );
bool stream = json_value ( data , "stream" , false );
const auto task_ids = server_task :: get_list_id ( tasks );
const auto completion_id = gen_chatcmplid ();
if ( ! stream ) {
2024-09-15 20:46:12 +03:00
ctx_server . receive_cmpl_results ( task_ids , [ & ]( const std :: vector < server_task_result > & results ) {
2024-09-02 17:11:51 +02:00
// multitask is never support in chat completion, there is only one result
2024-09-15 20:46:12 +03:00
json result_oai = format_final_response_oaicompat ( data , results [ 0 ]. data , completion_id , /*.streaming =*/ false , verbose );
2024-09-02 17:11:51 +02:00
res_ok ( res , result_oai );
2024-09-15 20:46:12 +03:00
}, [ & ]( const json & error_data ) {
2024-09-02 17:11:51 +02:00
res_error ( res , error_data );
});
2024-09-19 12:44:53 +03:00
ctx_server . queue_results . remove_waiting_task_ids ( task_ids );
2024-09-02 17:11:51 +02:00
} else {
const auto chunked_content_provider = [ task_ids , & ctx_server , completion_id ]( size_t , httplib :: DataSink & sink ) {
2024-09-15 20:46:12 +03:00
ctx_server . receive_cmpl_results_stream ( task_ids , [ & ]( const server_task_result & result ) -> bool {
2024-09-02 17:11:51 +02:00
std :: vector < json > result_array = format_partial_response_oaicompat ( result . data , completion_id );
for ( auto & event_data : result_array ) {
if ( event_data . empty ()) {
continue ; // skip the stop token
}
if ( ! server_sent_event ( sink , "data" , event_data )) {
return false ; // connection is closed
}
}
return true ; // ok
2024-09-15 20:46:12 +03:00
}, [ & ]( const json & error_data ) {
2024-09-02 17:11:51 +02:00
server_sent_event ( sink , "error" , error_data );
});
2024-09-14 17:36:44 +08:00
static const std :: string ev_done = "data: [DONE] \n\n " ;
sink . write ( ev_done . data (), ev_done . size ());
2024-09-02 17:11:51 +02:00
sink . done ();
2024-03-07 11:41:53 +02:00
return true ;
};
2024-09-19 12:44:53 +03:00
auto on_complete = [ task_ids , & ctx_server ] ( bool ) {
ctx_server . queue_results . remove_waiting_task_ids ( task_ids );
};
res . set_chunked_content_provider ( "text/event-stream" , chunked_content_provider , on_complete );
2024-03-07 11:41:53 +02:00
}
2024-03-07 19:42:39 +09:00
};
2024-08-16 17:19:05 +02:00
const auto handle_models = [ & params , & ctx_server ]( const httplib :: Request & , httplib :: Response & res ) {
2024-03-07 11:41:53 +02:00
json models = {
{ "object" , "list" },
{ "data" , {
2024-09-06 23:21:29 +02:00
{
{ "id" , params . model_alias },
{ "object" , "model" },
{ "created" , std :: time ( 0 )},
{ "owned_by" , "llamacpp" },
{ "meta" , ctx_server . model_meta ()}
},
2024-03-07 11:41:53 +02:00
}}
};
2024-08-16 17:19:05 +02:00
res . set_content ( models . dump (), MIMETYPE_JSON );
2024-03-09 11:27:53 +01:00
};
2024-03-07 11:41:53 +02:00
2024-09-02 17:11:51 +02:00
const auto handle_tokenize = [ & ctx_server , & res_ok ]( const httplib :: Request & req , httplib :: Response & res ) {
2024-03-07 11:41:53 +02:00
const json body = json :: parse ( req . body );
2024-09-12 22:30:11 +02:00
json tokens_response = json :: array ();
2024-03-07 11:41:53 +02:00
if ( body . count ( "content" ) != 0 ) {
2024-05-08 14:27:58 +02:00
const bool add_special = json_value ( body , "add_special" , false );
2024-09-12 22:30:11 +02:00
const bool with_pieces = json_value ( body , "with_pieces" , false );
2024-10-12 08:21:51 +03:00
2024-10-24 21:51:22 +02:00
llama_tokens tokens = tokenize_mixed ( ctx_server . ctx , body . at ( "content" ), add_special , true );
2024-09-12 22:30:11 +02:00
if ( with_pieces ) {
for ( const auto & token : tokens ) {
2024-10-10 22:57:42 +02:00
std :: string piece = common_token_to_piece ( ctx_server . ctx , token );
2024-09-12 22:30:11 +02:00
json piece_json ;
// Check if the piece is valid UTF-8
if ( is_valid_utf8 ( piece )) {
piece_json = piece ;
} else {
// If not valid UTF-8, store as array of byte values
piece_json = json :: array ();
for ( unsigned char c : piece ) {
piece_json . push_back ( static_cast < int > ( c ));
}
}
tokens_response . push_back ({
{ "id" , token },
{ "piece" , piece_json }
});
}
} else {
tokens_response = tokens ;
}
2024-03-07 11:41:53 +02:00
}
2024-09-12 22:30:11 +02:00
const json data = format_tokenizer_response ( tokens_response );
2024-09-02 17:11:51 +02:00
res_ok ( res , data );
2024-03-09 11:27:53 +01:00
};
2024-03-07 11:41:53 +02:00
2024-09-02 17:11:51 +02:00
const auto handle_detokenize = [ & ctx_server , & res_ok ]( const httplib :: Request & req , httplib :: Response & res ) {
2024-03-07 11:41:53 +02:00
const json body = json :: parse ( req . body );
std :: string content ;
if ( body . count ( "tokens" ) != 0 ) {
2024-10-24 21:51:22 +02:00
const llama_tokens tokens = body . at ( "tokens" );
2024-03-07 11:41:53 +02:00
content = tokens_to_str ( ctx_server . ctx , tokens . cbegin (), tokens . cend ());
}
const json data = format_detokenized_response ( content );
2024-09-02 17:11:51 +02:00
res_ok ( res , data );
2024-03-09 11:27:53 +01:00
};
2024-03-07 11:41:53 +02:00
2024-09-02 17:11:51 +02:00
const auto handle_embeddings = [ & ctx_server , & res_error , & res_ok ]( const httplib :: Request & req , httplib :: Response & res ) {
2024-09-28 17:42:03 +03:00
// TODO: somehow clean up this checks in the future
if ( ! ctx_server . params . embedding || ctx_server . params . reranking ) {
res_error ( res , format_error_response ( "This server does not support embeddings. Start it with `--embeddings` and without `--reranking`" , ERROR_TYPE_NOT_SUPPORTED ));
return ;
}
2024-03-07 11:41:53 +02:00
const json body = json :: parse ( req . body );
2024-03-09 11:27:53 +01:00
bool is_openai = false ;
2024-03-07 11:41:53 +02:00
2024-03-13 11:39:11 +01:00
// an input prompt can be a string or a list of tokens (integer)
json prompt ;
2024-03-07 11:41:53 +02:00
if ( body . count ( "input" ) != 0 ) {
2024-03-09 11:27:53 +01:00
is_openai = true ;
2024-05-08 21:53:08 +02:00
prompt = body . at ( "input" );
2024-03-09 11:27:53 +01:00
} else if ( body . count ( "content" ) != 0 ) {
2024-03-13 11:39:11 +01:00
// with "content", we only support single prompt
2024-05-08 21:53:08 +02:00
prompt = std :: vector < std :: string > { body . at ( "content" )};
2024-03-07 11:41:53 +02:00
} else {
2024-03-11 10:56:41 +01:00
res_error ( res , format_error_response ( " \" input \" or \" content \" must be provided" , ERROR_TYPE_INVALID_REQUEST ));
return ;
2024-03-07 11:41:53 +02:00
}
2024-03-13 11:39:11 +01:00
// create and queue the task
2024-09-02 17:11:51 +02:00
json responses = json :: array ();
bool error = false ;
2024-03-13 11:39:11 +01:00
{
2024-10-24 21:51:22 +02:00
std :: vector < server_task > tasks = ctx_server . create_tasks_inference ({{ "prompt" , prompt }}, SERVER_TASK_INF_TYPE_EMBEDDING );
2024-09-02 17:11:51 +02:00
ctx_server . queue_results . add_waiting_tasks ( tasks );
ctx_server . queue_tasks . post ( tasks );
2024-03-07 11:41:53 +02:00
2024-03-09 11:27:53 +01:00
// get the result
2024-09-02 17:11:51 +02:00
std :: unordered_set < int > task_ids = server_task :: get_list_id ( tasks );
ctx_server . receive_cmpl_results ( task_ids , [ & ]( std :: vector < server_task_result > & results ) {
for ( const auto & res : results ) {
responses . push_back ( res . data );
2024-03-13 11:39:11 +01:00
}
2024-09-15 20:46:12 +03:00
}, [ & ]( const json & error_data ) {
2024-09-02 17:11:51 +02:00
res_error ( res , error_data );
error = true ;
});
2024-09-19 12:44:53 +03:00
ctx_server . queue_results . remove_waiting_task_ids ( task_ids );
2024-09-02 17:11:51 +02:00
}
if ( error ) {
return ;
2024-03-09 11:27:53 +01:00
}
2024-03-07 11:41:53 +02:00
2024-03-09 11:27:53 +01:00
// write JSON response
2024-03-13 11:39:11 +01:00
json root = is_openai
? format_embeddings_response_oaicompat ( body , responses )
: responses [ 0 ];
2024-09-02 17:11:51 +02:00
res_ok ( res , root );
2024-03-09 11:27:53 +01:00
};
2023-10-22 22:53:08 +03:00
2024-09-28 17:42:03 +03:00
const auto handle_rerank = [ & ctx_server , & res_error , & res_ok ]( const httplib :: Request & req , httplib :: Response & res ) {
if ( ! ctx_server . params . reranking ) {
res_error ( res , format_error_response ( "This server does not support reranking. Start it with `--reranking`" , ERROR_TYPE_NOT_SUPPORTED ));
return ;
}
const json body = json :: parse ( req . body );
// TODO: implement
//int top_n = 1;
//if (body.count("top_n") != 1) {
// top_n = body.at("top_n");
//} else {
// res_error(res, format_error_response("\"top_n\" must be provided", ERROR_TYPE_INVALID_REQUEST));
// return;
//}
json query ;
if ( body . count ( "query" ) == 1 ) {
query = body . at ( "query" );
if ( ! query . is_string ()) {
res_error ( res , format_error_response ( " \" query \" must be a string" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
} else {
res_error ( res , format_error_response ( " \" query \" must be provided" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
std :: vector < std :: string > documents = json_value ( body , "documents" , std :: vector < std :: string > ());
if ( documents . empty ()) {
res_error ( res , format_error_response ( " \" documents \" must be a non-empty string array" , ERROR_TYPE_INVALID_REQUEST ));
return ;
}
// construct prompt object: array of ["query", "doc0", "doc1", ...]
json prompt ;
prompt . push_back ( query );
for ( const auto & doc : documents ) {
prompt . push_back ( doc );
}
LOG_DBG ( "rerank prompt: %s \n " , prompt . dump (). c_str ());
// create and queue the task
json responses = json :: array ();
bool error = false ;
{
2024-10-24 21:51:22 +02:00
std :: vector < server_task > tasks = ctx_server . create_tasks_inference ({{ "prompt" , prompt }}, SERVER_TASK_INF_TYPE_RERANK );
2024-09-28 17:42:03 +03:00
ctx_server . queue_results . add_waiting_tasks ( tasks );
ctx_server . queue_tasks . post ( tasks );
// get the result
std :: unordered_set < int > task_ids = server_task :: get_list_id ( tasks );
ctx_server . receive_cmpl_results ( task_ids , [ & ]( std :: vector < server_task_result > & results ) {
for ( const auto & res : results ) {
responses . push_back ( res . data );
}
}, [ & ]( const json & error_data ) {
res_error ( res , error_data );
error = true ;
});
}
if ( error ) {
return ;
}
// write JSON response
json root = format_response_rerank ( body , responses );
res_ok ( res , root );
};
2024-08-16 17:19:05 +02:00
const auto handle_lora_adapters_list = [ & ]( const httplib :: Request & , httplib :: Response & res ) {
2024-08-06 17:33:39 +02:00
json result = json :: array ();
2024-09-15 20:46:12 +03:00
for ( size_t i = 0 ; i < ctx_server . loras . size (); ++ i ) {
auto & lora = ctx_server . loras [ i ];
2024-08-06 17:33:39 +02:00
result . push_back ({
{ "id" , i },
2024-09-15 20:46:12 +03:00
{ "path" , lora . path },
{ "scale" , lora . scale },
2024-08-06 17:33:39 +02:00
});
}
2024-09-02 17:11:51 +02:00
res_ok ( res , result );
2024-08-06 17:33:39 +02:00
res . status = 200 ; // HTTP OK
};
const auto handle_lora_adapters_apply = [ & ]( const httplib :: Request & req , httplib :: Response & res ) {
const std :: vector < json > body = json :: parse ( req . body );
2024-09-15 20:46:12 +03:00
int max_idx = ctx_server . loras . size ();
2024-08-06 17:33:39 +02:00
// clear existing value
2024-09-15 20:46:12 +03:00
for ( auto & lora : ctx_server . loras ) {
lora . scale = 0.0f ;
2024-08-06 17:33:39 +02:00
}
// set value
for ( auto entry : body ) {
int id = entry . at ( "id" );
float scale = entry . at ( "scale" );
if ( 0 <= id && id < max_idx ) {
2024-09-15 20:46:12 +03:00
ctx_server . loras [ id ]. scale = scale ;
2024-08-06 17:33:39 +02:00
} else {
throw std :: runtime_error ( "invalid adapter id" );
}
}
server_task task ;
task . type = SERVER_TASK_TYPE_SET_LORA ;
const int id_task = ctx_server . queue_tasks . post ( task );
ctx_server . queue_results . add_waiting_task_id ( id_task );
server_task_result result = ctx_server . queue_results . recv ( id_task );
ctx_server . queue_results . remove_waiting_task_id ( id_task );
2024-09-02 17:11:51 +02:00
res_ok ( res , result . data );
2024-08-06 17:33:39 +02:00
res . status = 200 ; // HTTP OK
};
2024-03-13 11:39:11 +01:00
auto handle_static_file = []( unsigned char * content , size_t len , const char * mime_type ) {
return [ content , len , mime_type ]( const httplib :: Request & , httplib :: Response & res ) {
res . set_content ( reinterpret_cast < const char *> ( content ), len , mime_type );
return false ;
};
};
2024-03-09 11:27:53 +01:00
//
// Router
//
// register static assets routes
2024-06-04 21:23:39 +03:00
if ( ! params . public_path . empty ()) {
2024-03-09 11:27:53 +01:00
// Set the base directory for serving static files
2024-06-04 21:23:39 +03:00
svr -> set_base_dir ( params . public_path );
2024-03-09 11:27:53 +01:00
}
2024-06-04 21:23:39 +03:00
2024-10-08 13:27:04 +02:00
if ( ! params . api_keys . empty ()) {
// for now, if API key is set, web UI is unusable
svr -> Get ( "/" , [ & ]( const httplib :: Request & , httplib :: Response & res ) {
return res . set_content ( "Web UI is disabled because API key is set." , "text/html; charset=utf-8" );
});
} else {
// using embedded static files
svr -> Get ( "/" , handle_static_file ( index_html , index_html_len , "text/html; charset=utf-8" ));
svr -> Get ( "/index.js" , handle_static_file ( index_js , index_js_len , "text/javascript; charset=utf-8" ));
svr -> Get ( "/completion.js" , handle_static_file ( completion_js , completion_js_len , "text/javascript; charset=utf-8" ));
svr -> Get ( "/json-schema-to-grammar.mjs" , handle_static_file ( json_schema_to_grammar_mjs , json_schema_to_grammar_mjs_len , "text/javascript; charset=utf-8" ));
2024-06-01 21:31:48 +02:00
2024-10-08 13:27:04 +02:00
// add new-ui files
svr -> Get ( "/colorthemes.css" , handle_static_file ( colorthemes_css , colorthemes_css_len , "text/css; charset=utf-8" ));
svr -> Get ( "/style.css" , handle_static_file ( style_css , style_css_len , "text/css; charset=utf-8" ));
svr -> Get ( "/theme-beeninorder.css" , handle_static_file ( theme_beeninorder_css , theme_beeninorder_css_len , "text/css; charset=utf-8" ));
svr -> Get ( "/theme-ketivah.css" , handle_static_file ( theme_ketivah_css , theme_ketivah_css_len , "text/css; charset=utf-8" ));
svr -> Get ( "/theme-mangotango.css" , handle_static_file ( theme_mangotango_css , theme_mangotango_css_len , "text/css; charset=utf-8" ));
svr -> Get ( "/theme-playground.css" , handle_static_file ( theme_playground_css , theme_playground_css_len , "text/css; charset=utf-8" ));
svr -> Get ( "/theme-polarnight.css" , handle_static_file ( theme_polarnight_css , theme_polarnight_css_len , "text/css; charset=utf-8" ));
svr -> Get ( "/theme-snowstorm.css" , handle_static_file ( theme_snowstorm_css , theme_snowstorm_css_len , "text/css; charset=utf-8" ));
svr -> Get ( "/index-new.html" , handle_static_file ( index_new_html , index_new_html_len , "text/html; charset=utf-8" ));
svr -> Get ( "/system-prompts.js" , handle_static_file ( system_prompts_js , system_prompts_js_len , "text/javascript; charset=utf-8" ));
svr -> Get ( "/prompt-formats.js" , handle_static_file ( prompt_formats_js , prompt_formats_js_len , "text/javascript; charset=utf-8" ));
}
2024-03-09 11:27:53 +01:00
// register API routes
2024-10-08 13:27:04 +02:00
svr -> Get ( "/health" , handle_health ); // public endpoint (no API key check)
2024-03-09 11:27:53 +01:00
svr -> Get ( "/metrics" , handle_metrics );
svr -> Get ( "/props" , handle_props );
2024-10-08 13:27:04 +02:00
svr -> Post ( "/props" , handle_props_change );
svr -> Get ( "/models" , handle_models ); // public endpoint (no API key check)
svr -> Get ( "/v1/models" , handle_models ); // public endpoint (no API key check)
2024-03-09 11:27:53 +01:00
svr -> Post ( "/completion" , handle_completions ); // legacy
svr -> Post ( "/completions" , handle_completions );
svr -> Post ( "/v1/completions" , handle_completions );
svr -> Post ( "/chat/completions" , handle_chat_completions );
svr -> Post ( "/v1/chat/completions" , handle_chat_completions );
svr -> Post ( "/infill" , handle_infill );
svr -> Post ( "/embedding" , handle_embeddings ); // legacy
svr -> Post ( "/embeddings" , handle_embeddings );
svr -> Post ( "/v1/embeddings" , handle_embeddings );
2024-09-28 17:42:03 +03:00
svr -> Post ( "/rerank" , handle_rerank );
svr -> Post ( "/reranking" , handle_rerank );
svr -> Post ( "/v1/rerank" , handle_rerank );
svr -> Post ( "/v1/reranking" , handle_rerank );
2024-03-09 11:27:53 +01:00
svr -> Post ( "/tokenize" , handle_tokenize );
svr -> Post ( "/detokenize" , handle_detokenize );
2024-08-06 17:33:39 +02:00
// LoRA adapters hotswap
svr -> Get ( "/lora-adapters" , handle_lora_adapters_list );
svr -> Post ( "/lora-adapters" , handle_lora_adapters_apply );
// Save & load slots
svr -> Get ( "/slots" , handle_slots );
2024-09-02 17:11:51 +02:00
svr -> Post ( "/slots/:id_slot" , handle_slots_action );
2024-03-09 11:27:53 +01:00
//
// Start the server
//
2024-06-04 21:23:39 +03:00
if ( params . n_threads_http < 1 ) {
2024-03-03 08:48:36 +01:00
// +2 threads for monitoring endpoints
2024-06-04 21:23:39 +03:00
params . n_threads_http = std :: max ( params . n_parallel + 2 , ( int32_t ) std :: thread :: hardware_concurrency () - 1 );
2024-03-01 10:08:08 +01:00
}
2024-06-04 21:23:39 +03:00
log_data [ "n_threads_http" ] = std :: to_string ( params . n_threads_http );
svr -> new_task_queue = [ & params ] { return new httplib :: ThreadPool ( params . n_threads_http ); };
2024-03-01 10:08:08 +01:00
2024-08-16 17:19:05 +02:00
// clean up function, to be called before exit
auto clean_up = [ & svr ]() {
svr -> stop ();
llama_backend_free ();
};
2024-03-07 11:41:53 +02:00
2024-08-16 17:19:05 +02:00
// bind HTTP listen port, run the HTTP server in a thread
if ( ! svr -> bind_to_port ( params . hostname , params . port )) {
2024-09-15 20:46:12 +03:00
//LOG_ERROR("couldn't bind HTTP server socket", {
// {"hostname", params.hostname},
// {"port", params.port},
//});
LOG_ERR ( "%s: couldn't bind HTTP server socket, hostname: %s, port: %d \n " , __func__ , params . hostname . c_str (), params . port );
2024-08-16 17:19:05 +02:00
clean_up ();
return 1 ;
}
std :: thread t ([ & ]() { svr -> listen_after_bind (); });
svr -> wait_until_ready ();
2024-09-15 20:46:12 +03:00
LOG_INF ( "%s: HTTP server is listening, hostname: %s, port: %d, http threads: %d \n " , __func__ , params . hostname . c_str (), params . port , params . n_threads_http );
2024-08-16 17:19:05 +02:00
// load the model
2024-09-15 20:46:12 +03:00
LOG_INF ( "%s: loading model \n " , __func__ );
2024-08-16 17:19:05 +02:00
if ( ! ctx_server . load_model ( params )) {
clean_up ();
t . join ();
2024-09-15 20:46:12 +03:00
LOG_ERR ( "%s: exiting due to model loading error \n " , __func__ );
2024-08-16 17:19:05 +02:00
return 1 ;
}
2024-02-18 08:23:16 -08:00
2024-09-15 20:46:12 +03:00
ctx_server . init ();
state . store ( SERVER_STATE_READY );
LOG_INF ( "%s: model loaded \n " , __func__ );
// if a custom chat template is not supplied, we will use the one that comes with the model (if any)
if ( params . chat_template . empty ()) {
if ( ! ctx_server . validate_model_chat_template ()) {
LOG_WRN ( "%s: The chat template that comes with this model is not yet supported, falling back to chatml. This may cause the model to output suboptimal responses \n " , __func__ );
params . chat_template = "chatml" ;
}
}
// print sample chat example to make it clear which template is used
2024-10-10 22:57:42 +02:00
LOG_INF ( "%s: chat template, built_in: %d, chat_example: '%s' \n " , __func__ , params . chat_template . empty (), common_chat_format_example ( ctx_server . model , params . chat_template ). c_str ());
2024-09-15 20:46:12 +03:00
ctx_server . queue_tasks . on_new_task ( std :: bind (
& server_context :: process_single_task , & ctx_server , std :: placeholders :: _1 ));
2024-10-13 18:52:48 +03:00
2024-09-15 20:46:12 +03:00
ctx_server . queue_tasks . on_update_slots ( std :: bind (
& server_context :: update_slots , & ctx_server ));
shutdown_handler = [ & ]( int ) {
ctx_server . queue_tasks . terminate ();
};
2024-10-31 06:02:35 -07:00
LOG_INF ( "%s: server is listening on http://%s:%d - starting the main loop \n " , __func__ , params . hostname . c_str (), params . port );
2024-09-15 20:46:12 +03:00
ctx_server . queue_tasks . start_loop ();
2024-02-18 08:23:16 -08:00
#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 );
2024-03-28 16:50:48 +08:00
sigaction ( SIGTERM , & sigint_action , NULL );
2024-02-18 08:23:16 -08:00
#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
2024-03-07 11:41:53 +02:00
2024-08-16 17:19:05 +02:00
clean_up ();
2023-10-22 22:53:08 +03:00
t . join ();
2023-06-17 07:53:04 -04:00
return 0 ;
2023-05-21 11:51:18 -06:00
}