char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
acecd5603
char/aliceai-t5-moe
1#include "server-schema.h" 2 3#include "json-schema-to-grammar.h" 4 5namespace server_schema { 6 7// 8// llama.cpp-specific completion schema 9// 10 11std::vector < std::unique_ptr < field >> make_llama_cmpl_schema (const common_params & params_base ,task_params & params ) { 12 std::vector < std::unique_ptr < field >> fields ; 13auto add = [& ](field * f ) { 14fields .emplace_back (f ); 15 }; 16 17add ((new field_bool ("verbose" ,params .verbose )) 18-> set_desc ("Include __verbose field in the response with additional debug information" )); 19 20add ((new field_bool ("timings_per_token" ,params .timings_per_token )) 21-> set_desc ("Include prompt processing and text generation speed information in each response" )); 22 23add ((new field_bool ("stream" ,params .stream )) 24-> set_desc ("Allows receiving each predicted token in real-time instead of waiting for the completion to finish" )); 25 26add ((new field_nested ("stream_options" )) 27-> add_subfield ((new field_bool ("include_usage" ,params .include_usage )) 28-> set_desc ("Whether to include usage information in the stream" )) 29-> set_desc ("Additional options for streaming responses" )); 30 31add ((new field_bool ("cache_prompt" ,params .cache_prompt )) 32-> set_desc ("Re-use KV cache from a previous request if possible. This way the common prefix does not have to be re-processed, only the suffix that differs between the requests" )); 33 34add ((new field_bool ("return_tokens" ,params .return_tokens )) 35-> set_desc ("Return the raw generated token ids in the `tokens` field" )); 36 37add ((new field_bool ("return_progress" ,params .return_progress )) 38-> set_desc ("Include prompt processing progress events in stream mode" )); 39 40add ((new field_num ("sse_ping_interval" ,params .sse_ping_interval )) 41-> set_hard_limits (-1 ,INT32_MAX ) 42-> set_desc ("Interval in seconds between SSE comment pings emitted while the stream stays silent, -1 disables pings" )); 43 44add ((new field_num ("n_predict" ,params .n_predict )) 45-> set_hard_limits (-1 ,INT32_MAX ) 46-> add_alias ("max_completion_tokens" ) 47-> add_alias ("max_tokens" ) 48-> set_desc ("Set the maximum number of tokens to predict. When 0, no tokens will be generated but the prompt is evaluated into the cache" )); 49 50add ((new field_num ("n_indent" ,params .n_indent )) 51-> set_hard_limits (0 ,INT32_MAX ) 52-> set_desc ("Specify the minimum line indentation for the generated text in number of whitespace characters. Useful for code completion tasks" )); 53 54add ((new field_num ("n_keep" ,params .n_keep )) 55-> set_hard_limits (-1 ,INT32_MAX ) 56-> set_desc ("Specify the number of tokens from the initial prompt to retain when context size is exceeded. Use -1 to retain all tokens from the prompt" )); 57 58add ((new field_num ("n_discard" ,params .n_discard )) 59-> set_hard_limits (0 ,INT32_MAX ) 60-> set_desc ("Number of tokens after n_keep that may be discarded when shifting context (0 = half context)" )); 61 62add ((new field_num ("n_cmpl" ,params .n_cmpl )) 63-> set_hard_limits (1 ,params_base .n_parallel ) 64-> add_alias ("n" )// alias "n" as fallback (OpenAI completions API) 65-> set_desc ("Number of completions to generate. If the input has multiple prompts, total outputs will be N prompts times n_cmpl" )); 66 67add ((new field_num ("n_cache_reuse" ,params .n_cache_reuse )) 68-> set_hard_limits (0 ,INT32_MAX ) 69-> set_desc ("Min chunk size to attempt reusing from the cache via KV shifting. See --cache-reuse arg" )); 70 71// TODO: implement t_max_prompt_ms 72// add((new field_num("t_max_prompt_ms", params.t_max_prompt_ms)) 73 74add ((new field_num ("t_max_predict_ms" ,params .t_max_predict_ms )) 75-> set_hard_limits (-1 , std::numeric_limits < int64_t > ::max ()) 76-> set_desc ("Set a time limit in milliseconds for the prediction phase. The timeout triggers if generation exceeds this time (measured since the first token) and a newline has been generated. Useful for FIM applications" )); 77 78add ((new field_json ("response_fields" )) 79-> set_desc ("A list of response fields to return. Missing fields are omitted without error. Fields with a slash are unnested (e.g. generation_settings/n_predict moves n_predict to the root)" ) 80-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 81ctx .params .response_fields = json_value (data ,"response_fields" , std::vector < std::string > ()); 82 })); 83 84 85// 86// Sampling params 87// 88 89add ((new field_num ("top_k" ,params .sampling .top_k )) 90-> set_limits (0 ,INT32_MAX ) 91-> set_desc ("Limit the next token selection to the K most probable tokens (0 = disabled)" )); 92 93add ((new field_num ("top_p" ,params .sampling .top_p )) 94-> set_limits (0.0f ,1.0f ) 95-> set_desc ("Limit the next token selection to a subset of tokens with cumulative probability above threshold P (1.0 = disabled)" )); 96 97add ((new field_num ("min_p" ,params .sampling .min_p )) 98-> set_limits (0.0f ,1.0f ) 99-> set_desc ("The minimum probability for a token to be considered, relative to the probability of the most likely token (0 = disabled)" )); 100 101add ((new field_num ("top_n_sigma" ,params .sampling .top_n_sigma )) 102-> set_desc ("Keep tokens within n standard deviations of the top token logit (< 0 = disabled)" )); 103 104add ((new field_num ("xtc_probability" ,params .sampling .xtc_probability )) 105-> set_limits (0.0f ,1.0f ) 106-> set_desc ("Set the chance for token removal via XTC sampler (0 = disabled)" )); 107 108add ((new field_num ("xtc_threshold" ,params .sampling .xtc_threshold )) 109-> set_limits (0.0f ,1.0f ) 110-> set_desc ("Set a minimum probability threshold for tokens to be removed via XTC sampler (> 0.5 disables XTC)" )); 111 112add ((new field_num ("typical_p" ,params .sampling .typ_p )) 113// ->set_limits(0.0f, 1.0f) // what's the valid range? 114-> set_desc ("Enable locally typical sampling with parameter p (1.0 = disabled)" )); 115 116add ((new field_num ("temperature" ,params .sampling .temp )) 117-> set_limits (0.0f , std::numeric_limits < float > ::infinity ()) 118-> set_desc ("Adjust the randomness of the generated text (0 = greedy)" )); 119 120add ((new field_num ("dynatemp_range" ,params .sampling .dynatemp_range )) 121-> set_desc ("Dynamic temperature range. The final temperature will be in [temperature - range, temperature + range] (0 = disabled)" )); 122 123add ((new field_num ("dynatemp_exponent" ,params .sampling .dynatemp_exponent )) 124-> set_desc ("Dynamic temperature exponent, controls how entropy maps to temperature" )); 125 126add ((new field_num ("repeat_last_n" ,params .sampling .penalty_last_n )) 127-> set_hard_limits (0 ,INT32_MAX ) 128-> set_desc ("Last n tokens to consider for penalizing repetition (0 = disabled)" )); 129 130add ((new field_num ("repeat_penalty" ,params .sampling .penalty_repeat )) 131-> set_desc ("Control the repetition of token sequences in the generated text (1.0 = disabled)" )); 132 133add ((new field_num ("frequency_penalty" ,params .sampling .penalty_freq )) 134-> set_desc ("Repeat alpha frequency penalty (0 = disabled)" )); 135 136add ((new field_num ("presence_penalty" ,params .sampling .penalty_present )) 137-> set_desc ("Repeat alpha presence penalty (0 = disabled)" )); 138 139add ((new field_num ("dry_multiplier" ,params .sampling .dry_multiplier )) 140-> set_desc ("Set the DRY (Don't Repeat Yourself) repetition penalty multiplier (0 = disabled)" )); 141 142add ((new field_num ("dry_base" ,params .sampling .dry_base )) 143-> set_desc ("Set the DRY repetition penalty base value (must be >= 1.0, any values < 1.0 will be replaced with the default value)" ) 144-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 145float v = data .at ("dry_base" ).get < float > (); 146ctx .params .sampling .dry_base = (v < 1.0f ) ?params_base .sampling .dry_base :v ; 147 })); 148 149add ((new field_num ("dry_allowed_length" ,params .sampling .dry_allowed_length )) 150-> set_hard_limits (0 ,INT32_MAX ) 151-> set_desc ("Tokens that extend repetition beyond this length receive exponentially increasing penalty: multiplier * base ^ (sequence_length - allowed_length)" )); 152 153add ((new field_num ("dry_penalty_last_n" ,params .sampling .dry_penalty_last_n )) 154-> set_hard_limits (0 ,INT32_MAX ) 155-> set_desc ("How many tokens to scan for repetitions (0 = disabled)" )); 156 157add ((new field_num ("mirostat" ,params .sampling .mirostat )) 158-> set_limits (0 ,2 ) 159-> set_desc ("Enable Mirostat sampling, controlling perplexity during text generation (0 = disabled, 1 = Mirostat, 2 = Mirostat 2.0)" )); 160 161add ((new field_num ("mirostat_tau" ,params .sampling .mirostat_tau )) 162-> set_desc ("Set the Mirostat target entropy, parameter tau" )); 163 164add ((new field_num ("mirostat_eta" ,params .sampling .mirostat_eta )) 165-> set_desc ("Set the Mirostat learning rate, parameter eta" )); 166 167add ((new field_num ("adaptive_target" ,params .sampling .adaptive_target )) 168-> set_limits (- std::numeric_limits < float > ::max (),1.0f ) 169-> set_desc ("Adaptive sampling target entropy (valid range 0.0 to 1.0; negative = disabled)" )); 170 171add ((new field_num ("adaptive_decay" ,params .sampling .adaptive_decay )) 172-> set_hard_limits (0.0f ,0.99f ) 173-> set_desc ("EMA decay for adaptive sampling; history approximates 1/(1-decay) tokens" )); 174 175// seed is uint32_t; field_num uses int32_t so use a handler 176add ((new field_num ("seed" ,params .sampling .seed )) 177-> set_desc ("Set the random number generator (RNG) seed (-1 = random)" )); 178 179add ((new field_num ("n_probs" ,params .sampling .n_probs )) 180-> add_alias ("logprobs" )// use "logprobs" if "n_probs" wasn't provided 181-> set_desc ("If greater than 0, output the probabilities of top N tokens for each generated token" )); 182 183add ((new field_num ("min_keep" ,params .sampling .min_keep )) 184-> set_hard_limits (0 ,INT32_MAX ) 185-> set_desc ("If greater than 0, force samplers to return at least N possible tokens" )); 186 187add ((new field_bool ("backend_sampling" ,params .sampling .backend_sampling )) 188-> set_desc ("Use backend sampling instead of llama.cpp sampling" )); 189 190add ((new field_bool ("post_sampling_probs" ,params .post_sampling_probs )) 191-> set_desc ("Return probabilities of top n_probs tokens after applying the sampling chain" )); 192 193// 194// Speculative decoding params 195// 196 197// TODO: to keep things simple, we disable speculative parameter adjustments for now 198#if 0 199// TODO: for now, be able to adjust only the draft-model based speculative parameters 200add ((new field_num ("speculative.n_max" ,params .speculative .draft .n_max )) 201-> set_hard_limits (0 ,INT32_MAX ) 202-> set_desc ("Maximum number of tokens to draft during speculative decoding" )); 203 204add ((new field_num ("speculative.n_min" ,params .speculative .draft .n_min )) 205-> set_hard_limits (0 ,INT32_MAX ) 206-> set_desc ("Minimum number of draft tokens to use for speculative decoding" ); 207 208add ((new field_num ("speculative.p_min" ,params .speculative .draft .p_min )) 209-> set_hard_limits (0.0f ,1.0f ) 210-> set_desc ("Minimum speculative decoding probability for draft tokens (0 = greedy)" )); 211 212 213add ((new field_str ("speculative.type" )) 214-> set_desc ("Speculative decoding method (for debugging and research purposes)" ) 215-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 216ctx .params .speculative .types = {common_speculative_type_from_name (data .at ("speculative.type" ).get < std::string > ()) }; 217 })); 218 219add ((new field_num ("speculative.ngram_size_n" ,params .speculative .ngram_simple .size_n )) 220-> set_desc ("Ngram size for lookup in ngram-based speculative decoding" )); 221 222add ((new field_num ("speculative.ngram_size_m" ,params .speculative .ngram_simple .size_m )) 223-> set_desc ("Mgram size for speculative tokens in ngram-based speculative decoding" )); 224 225add ((new field_num ("speculative.ngram_min_hits" ,params .speculative .ngram_simple .min_hits )) 226-> set_desc ("Minimum hits at ngram lookup for mgram to be proposed" )); 227#endif 228 229add ((new field_json ("lora" )) 230-> set_desc ("A list of LoRA adapters to apply to this request. Each entry must have `id` and `scale` fields. Adapters not listed default to scale 0.0" ) 231-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 232const auto & lora = data .at ("lora" ); 233if (!lora .is_array ()) { 234throw std::runtime_error ("Error: 'lora' must be an array of objects with 'id' and 'scale' fields" ); 235 } 236ctx .params .lora = parse_lora_request (lora ); 237 })); 238 239// sequence breakers for DRY 240// Currently, this is not compatible with TextGen WebUI, Koboldcpp and SillyTavern format 241// Ref: https://github.com/oobabooga/text-generation-webui/blob/d1af7a41ade7bd3c3a463bfa640725edb818ebaf/extensions/openai/typing.py#L39 242add ((new field_json ("dry_sequence_breakers" )) 243-> set_desc ("Specify an array of sequence breakers for DRY sampling. Only a JSON array of strings is accepted" ) 244-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 245ctx .params .sampling .dry_sequence_breakers = json_value (data ,"dry_sequence_breakers" , std::vector < std::string > ()); 246if (ctx .params .sampling .dry_sequence_breakers .empty ()) { 247throw std::runtime_error ("Error: dry_sequence_breakers must be a non-empty array of strings" ); 248 } 249 })); 250 251// handle both "json_schema" and "grammar" 252add ((new field_json ("json_schema" )) 253-> add_alias ("grammar" ) 254-> set_desc ("Set a JSON schema (json_schema) or GBNF grammar string (grammar) for constrained generation. json_schema takes precedence if both are provided" ) 255-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 256auto & params = ctx .params ; 257if (data .contains ("json_schema" )&& !data .contains ("grammar" )) { 258try { 259auto schema = json_value (data ,"json_schema" , json::object ()); 260if (schema .is_object ()&& schema .empty ()) { 261// an empty schema means any object 262schema ["type" ]= "object" ; 263 } 264SRV_DBG ("JSON schema: %s\n" ,schema .dump (2 ).c_str ()); 265 std::string grammar_str = json_schema_to_grammar (schema ); 266SRV_DBG ("Converted grammar: %s\n" ,grammar_str .c_str ()); 267params .sampling .grammar = {COMMON_GRAMMAR_TYPE_OUTPUT_FORMAT , std::move (grammar_str )}; 268 }catch (const std::exception & e ) { 269throw std::runtime_error (std::string ("\"json_schema\": " )+ e .what ()); 270 } 271 }else { 272 std::string grammar_str = json_value (data ,"grammar" , std::string ()); 273if (!grammar_str .empty ()) { 274// grammar_type key is set by the server when converting chat template grammars 275 std::string grammar_type = json_value (data ,"grammar_type" , std::string ()); 276if (grammar_type == "tool_calls" ) { 277params .sampling .grammar = {COMMON_GRAMMAR_TYPE_TOOL_CALLS , std::move (grammar_str )}; 278 }else { 279// explicit grammar from the user (API field "grammar") 280params .sampling .grammar = {COMMON_GRAMMAR_TYPE_USER , std::move (grammar_str )}; 281 } 282SRV_DBG ("Grammar (%s): %s\n" ,grammar_type .c_str (),common_grammar_value (params .sampling .grammar ).c_str ()); 283 } 284 } 285 })); 286 287add ((new field_bool ("grammar_lazy" ,params .sampling .grammar_lazy )) 288-> set_desc ("Whether to apply grammar constraints lazily, only when triggered (instead of at every step)" )); 289 290// 291// Chat parser params 292// 293 294// TODO: change this to string field instead 295add ((new field_json ("chat_format" )) 296-> set_desc ("Chat format used internally by the server" ) 297-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 298ctx .params .chat_parser_params .format = static_cast < common_chat_format > (data .at ("chat_format" ).get < int > ()); 299SRV_TRC ("chat format: %s\n" ,common_chat_format_name (ctx .params .chat_parser_params .format )); 300 })); 301 302add ((new field_str ("reasoning_format" )) 303-> set_desc ("Reasoning format for chain-of-thought models" ) 304-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 305auto reasoning_format = common_reasoning_format_from_name (data .at ("reasoning_format" ).get < std::string > ()); 306ctx .params .chat_parser_params .reasoning_format = reasoning_format ; 307ctx .params .chat_parser_params .reasoning_in_content = ctx .params .stream && (reasoning_format == COMMON_REASONING_FORMAT_DEEPSEEK_LEGACY ); 308 })); 309 310add ((new field_str ("generation_prompt" )) 311-> set_desc ("Generation prompt appended to the chat template output" ) 312-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 313 std::string s = data .at ("generation_prompt" ).get < std::string > (); 314ctx .params .chat_parser_params .generation_prompt = s ; 315ctx .params .sampling .generation_prompt = s ; 316 })); 317 318add ((new field_bool ("parse_tool_calls" ,params .chat_parser_params .parse_tool_calls )) 319-> set_desc ("Whether to parse tool calls from the generated output" )); 320 321add ((new field_str ("chat_parser" )) 322-> set_desc ("Chat parser configuration string" ) 323-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 324ctx .params .chat_parser_params .parser .load (data .at ("chat_parser" ).get < std::string > ()); 325 })); 326 327add ((new field_json ("continue_final_message" )) 328-> set_desc ("Whether to continue the final message of the chat template" ) 329-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 330auto continuation = common_chat_continuation_parse (data .at ("continue_final_message" )); 331ctx .params .chat_parser_params .is_continuation = continuation != COMMON_CHAT_CONTINUATION_NONE ; 332 })); 333 334add ((new field_bool ("echo" ,params .chat_parser_params .echo )) 335-> set_desc ("Whether to echo the input tokens in the output" )); 336 337// 338// Token-level fields (require vocab) 339// 340 341add ((new field_json ("preserved_tokens" )) 342-> set_desc ("List of token strings that must not be split during tokenization" ) 343-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 344GGML_ASSERT (ctx .vocab != nullptr ); 345for (const auto & t :data .at ("preserved_tokens" )) { 346auto ids = common_tokenize (ctx .vocab ,t .get < std::string > (), false, true); 347if (ids .size ()== 1 ) { 348ctx .params .sampling .preserved_tokens .insert (ids [0 ]); 349 } 350 } 351 })); 352 353add ((new field_json ("grammar_triggers" )) 354-> set_desc ("List of strings or patterns that trigger grammar-constrained generation" ) 355-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 356GGML_ASSERT (ctx .vocab != nullptr ); 357for (const auto & t :data .at ("grammar_triggers" )) { 358server_grammar_trigger ct (t ); 359if (ct .value .type == COMMON_GRAMMAR_TRIGGER_TYPE_WORD ) { 360const auto & word = ct .value .value ; 361auto ids = common_tokenize (ctx .vocab ,word , false, true); 362if (ids .size ()== 1 ) { 363auto token = ids [0 ]; 364if (std::find (ctx .params .sampling .preserved_tokens .begin (),ctx .params .sampling .preserved_tokens .end (), (llama_token )token )== ctx .params .sampling .preserved_tokens .end ()) { 365throw std::runtime_error ("Grammar trigger word should be marked as preserved token: " + word ); 366 } 367common_grammar_trigger trigger ; 368trigger .type = COMMON_GRAMMAR_TRIGGER_TYPE_TOKEN ; 369trigger .value = word ; 370trigger .token = token ; 371ctx .params .sampling .grammar_triggers .push_back (std::move (trigger )); 372 }else { 373ctx .params .sampling .grammar_triggers .push_back ({COMMON_GRAMMAR_TRIGGER_TYPE_WORD ,word }); 374 } 375 }else { 376ctx .params .sampling .grammar_triggers .emplace_back (std::move (ct .value )); 377 } 378 } 379if (ctx .params .sampling .grammar_lazy && ctx .params .sampling .grammar_triggers .empty ()) { 380throw std::runtime_error ("Error: no triggers set for lazy grammar!" ); 381 } 382 })); 383 384add ((new field_bool ("reasoning_control" ,params .sampling .reasoning_control )) 385-> set_desc ("Create the budget sampler on demand so reasoning can be ended at runtime" )); 386 387add ((new field_num ("reasoning_budget_tokens" ,params .sampling .reasoning_budget_tokens )) 388-> set_hard_limits (-1 ,INT32_MAX ) 389-> set_desc ("Number of tokens in the reasoning budget (-1 = disabled)" )); 390 391add ((new field_str ("reasoning_budget_start_tag" )) 392-> set_desc ("Token string marking the start of the reasoning budget section" ) 393-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 394GGML_ASSERT (ctx .vocab != nullptr ); 395ctx .params .sampling .reasoning_budget_start = common_tokenize (ctx .vocab ,data .at ("reasoning_budget_start_tag" ).get < std::string > (), false, true); 396 })); 397 398add ((new field_json ("reasoning_budget_end_tags" )) 399-> add_alias ("reasoning_budget_end_tag" ) 400-> set_desc ("Token strings marking the end of the reasoning budget section; the first is forced when the budget expires" ) 401-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 402GGML_ASSERT (ctx .vocab != nullptr ); 403ctx .params .sampling .reasoning_budget_end .clear (); 404if (data .contains ("reasoning_budget_end_tags" )) { 405for (const auto & t :data .at ("reasoning_budget_end_tags" )) { 406 std::string tag = t .get < std::string > (); 407if (!tag .empty ()) { 408ctx .params .sampling .reasoning_budget_end .push_back (common_tokenize (ctx .vocab ,tag , false, true)); 409 } 410 } 411 }else if (data .contains ("reasoning_budget_end_tag" )) { 412 std::string tag = data .at ("reasoning_budget_end_tag" ).get < std::string > (); 413if (!tag .empty ()) { 414ctx .params .sampling .reasoning_budget_end .push_back (common_tokenize (ctx .vocab ,tag , false, true)); 415 } 416 } 417 })); 418 419add ((new field_str ("reasoning_budget_message" )) 420-> set_desc ("Message to prepend to the reasoning budget end tag when forcing it" ) 421-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 422GGML_ASSERT (ctx .vocab != nullptr ); 423if (!ctx .params .sampling .reasoning_budget_end .empty ()) { 424llama_tokens end_tag = ctx .params .sampling .reasoning_budget_end .front (); 425 std::string message = json_value (data ,"reasoning_budget_message" , std::string ()); 426if (!message .empty ()) { 427llama_tokens message_tokens = common_tokenize (ctx .vocab ,message , false, true); 428end_tag .insert (end_tag .begin (),message_tokens .begin (),message_tokens .end ()); 429 } 430ctx .params .sampling .reasoning_budget_forced = std::move (end_tag ); 431 } 432 })); 433 434add ((new field_json ("logit_bias" )) 435-> set_desc ("Modify the likelihood of specific tokens. Accepts an array of [token, bias] pairs or an object mapping token to bias. Use false as bias to ban a token" ) 436-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 437GGML_ASSERT (ctx .vocab != nullptr ); 438ctx .params .sampling .logit_bias .clear (); 439const auto & logit_bias = data .at ("logit_bias" ); 440const int n_vocab = llama_vocab_n_tokens (ctx .vocab ); 441auto parse_bias = [](const json & v ,float & bias )-> bool { 442if (v .is_number ()) {bias = v .get < float > ();return true; } 443if (v .is_boolean ()&& !v .get < bool > ()) {bias = - INFINITY ;return true; } 444return false; 445 }; 446if (logit_bias .is_array ()) { 447for (const auto & el :logit_bias ) { 448if (!el .is_array ()|| el .size ()!= 2 )continue ; 449float bias ; 450if (!parse_bias (el [1 ],bias ))continue ; 451if (el [0 ].is_number_integer ()) { 452llama_token tok = el [0 ].get < llama_token > (); 453if (tok >=0 && tok < n_vocab )ctx .params .sampling .logit_bias .push_back ({tok ,bias }); 454 }else if (el [0 ].is_string ()) { 455for (auto tok :common_tokenize (ctx .vocab ,el [0 ].get < std::string > (), false)) 456ctx .params .sampling .logit_bias .push_back ({tok ,bias }); 457 } 458 } 459 }else if (logit_bias .is_object ()) { 460for (const auto & el :logit_bias .items ()) { 461float bias ; 462if (!parse_bias (el .value (),bias ))continue ; 463char * end ; 464llama_token tok = strtol (el .key ().c_str (),& end ,10 ); 465if (* end == 0 ) { 466if (tok >=0 && tok < n_vocab )ctx .params .sampling .logit_bias .push_back ({tok ,bias }); 467 }else { 468for (auto t :common_tokenize (ctx .vocab ,el .key (), false)) 469ctx .params .sampling .logit_bias .push_back ({t ,bias }); 470 } 471 } 472 } 473 })); 474 475add ((new field_bool ("ignore_eos" ,params .sampling .ignore_eos )) 476-> set_desc ("Ignore the end-of-sequence token and continue generating" ) 477-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 478GGML_ASSERT (ctx .logit_bias_eog != nullptr ); 479ctx .params .sampling .ignore_eos = data .at ("ignore_eos" ).get < bool > (); 480if (ctx .params .sampling .ignore_eos && ctx .logit_bias_eog ) { 481ctx .params .sampling .logit_bias .insert ( 482ctx .params .sampling .logit_bias .end (), 483ctx .logit_bias_eog -> begin (),ctx .logit_bias_eog -> end ()); 484 } 485 })); 486 487add ((new field_json ("stop" )) 488-> set_desc ("Specify stopping strings. Generation stops when one is produced, and the string is not included in the output" ) 489-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 490ctx .params .antiprompt .clear (); 491const auto & stop = data .at ("stop" ); 492if (stop .is_array ()) { 493for (const auto & word :stop ) { 494if (!word .empty ())ctx .params .antiprompt .push_back (word ); 495 } 496 }else if (stop .is_string ()) { 497ctx .params .antiprompt .push_back (stop .get < std::string > ()); 498 } 499// fall back to CLI defaults if the request provided no effective stop strings 500if (ctx .params .antiprompt .empty ()) { 501ctx .params .antiprompt = params_base .antiprompt ; 502 } 503 })); 504 505add ((new field_json ("samplers" )) 506-> set_desc ("The order in which samplers are applied. An array of sampler type names, or a single string of sampler chars" ) 507-> set_handler ([& ](field_eval_context & ctx ,const json & data ) { 508const auto & samplers = data .at ("samplers" ); 509if (samplers .is_array ()) { 510ctx .params .sampling .samplers = common_sampler_types_from_names (samplers .get < std::vector < std::string >> ()); 511 }else if (samplers .is_string ()) { 512ctx .params .sampling .samplers = common_sampler_types_from_chars (samplers .get < std::string > ()); 513 } 514 })); 515 516return fields ; 517} 518 519task_params eval_llama_cmpl_schema ( 520const llama_vocab * vocab , 521const common_params & params_base , 522const std::vector < llama_logit_bias > & logit_bias_eog , 523const json & data ) { 524task_params params ; 525 526// Sampling parameter defaults are loaded from the global server context (but individual requests can still override them) 527params .sampling = params_base .sampling ; 528params .speculative = params_base .speculative ; 529params .n_keep = params_base .n_keep ; 530params .n_predict = params_base .n_predict ; 531params .n_cache_reuse = params_base .n_cache_reuse ; 532params .cache_prompt = params_base .cache_prompt ; 533params .antiprompt = params_base .antiprompt ; 534params .sse_ping_interval = params_base .sse_ping_interval ; 535 536// enabling this will output extra debug information in the HTTP responses from the server 537params .verbose = params_base .verbosity > 9 ; 538 539params .chat_parser_params .reasoning_format = params_base .reasoning_format ; 540 541// create context and schema 542field_eval_context ctx (params ); 543ctx .vocab = vocab ; 544ctx .logit_bias_eog = & logit_bias_eog ; 545 546auto schema = make_llama_cmpl_schema (params_base ,params ); 547 548// eval all fields in the schema 549for (const auto & f :schema ) { 550f -> eval (ctx ,data ); 551 } 552 553// post-processing 554 { 555// if "reasoning_format" is not provided, its handler will not be called, we will need to handle it here 556auto reasoning_format = params .chat_parser_params .reasoning_format ; 557params .chat_parser_params .reasoning_in_content = params .stream && (reasoning_format == COMMON_REASONING_FORMAT_DEEPSEEK_LEGACY ); 558 } 559 560// debugging 561 { 562auto budget = params .sampling .reasoning_budget_tokens ; 563SRV_DBG ("reasoning budget: tokens=%d, generation_prompt='%s', start=%zu toks, end=%zu seqs, forced=%zu toks\n" , 564budget ,params .sampling .generation_prompt .c_str (), 565params .sampling .reasoning_budget_start .size (), 566params .sampling .reasoning_budget_end .size (), 567params .sampling .reasoning_budget_forced .size ()); 568 } 569 570return params ; 571} 572 573// 574// eval() implementations 575// 576 577static void handle_with_catch (const char * name , std::function < void ()> func ) { 578try { 579func (); 580 }catch (const std::exception & e ) { 581throw std::invalid_argument (string_format ("Field '%s': %s" ,name ,e .what ())); 582 } 583} 584 585// treat a null value as absent so clients can send null to request the server default 586static bool has_value (const json & data ,const char * n ) { 587return data .contains (n )&& !data .at (n ).is_null (); 588} 589 590template < typename T > 591void field_num < T > ::eval (field_eval_context & ctx ,const json & data ) { 592for (const auto & n :name ) { 593if (has_value (data ,n )) { 594handle_with_catch (n , [& ]() { 595if (custom_handler ) { 596custom_handler (ctx ,data ); 597 }else if (!is_hard_limit ) { 598val = std::max (min , std::min (max ,data .at (n ).template get < T > ())); 599 }else { 600T tmp = data .at (n ).template get < T > (); 601if (tmp < min || tmp > max ) { 602throw std::invalid_argument (std::string ("Value must be between " )+ std::to_string (min )+ " <= value <= " + std::to_string (max )+ ", but got " + std::to_string (tmp )); 603 } 604val = tmp ; 605 } 606 }); 607return ; 608 } 609 } 610} 611 612void field_str::eval (field_eval_context & ctx ,const json & data ) { 613GGML_ASSERT (custom_handler ); 614for (const auto & n :name ) { 615if (has_value (data ,n )) { 616handle_with_catch (n , [& ]() { 617custom_handler (ctx ,data ); 618 }); 619return ; 620 } 621 } 622} 623 624void field_bool::eval (field_eval_context & ctx ,const json & data ) { 625for (const auto & n :name ) { 626if (has_value (data ,n )) { 627handle_with_catch (n , [& ]() { 628if (custom_handler ) { 629custom_handler (ctx ,data ); 630 }else { 631val = data .at (n ).get < bool > (); 632 } 633 }); 634return ; 635 } 636 } 637} 638 639void field_json::eval (field_eval_context & ctx ,const json & data ) { 640GGML_ASSERT (custom_handler ); 641for (const auto & n :name ) { 642if (has_value (data ,n )) { 643handle_with_catch (n , [& ]() { 644custom_handler (ctx ,data ); 645 }); 646return ; 647 } 648 } 649} 650 651void field_nested::eval (field_eval_context & ctx ,const json & data ) { 652for (const auto & n :name ) { 653if (data .contains (n )&& data .at (n ).is_object ()) { 654for (auto & f :subfields ) { 655f -> eval (ctx ,data .at (n )); 656 } 657return ; 658 } 659 } 660} 661 662}// namespace server_schema