char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Charlotte Somenc/dec in server context8300f1411

char/aliceai-t5-moe
224.7 KiB5637 linesraw
1#include "server-context.h"
2#include "server-chat.h"
3#include "server-common.h"
4#include "server-http.h"
5#include "server-task.h"
6#include "server-queue.h"
7#include "server-schema.h"
8#include "server-stream.h"
9
10#include "build-info.h"
11#include "common.h"
12#include "fit.h"
13#include "llama.h"
14#include "log.h"
15#include "sampling.h"
16#include "speculative.h"
17#include "mtmd.h"
18#include "mtmd-helper.h"
19
20#include <algorithm>
21#include <cstddef>
22#include <cinttypes>
23#include <exception>
24#include <memory>
25#include <filesystem>
26#include <random>
27#include <utility>
28#include <fstream>
29
30// fix problem with std::min and std::max
31#if defined(_WIN32)
32#define WIN32_LEAN_AND_MEAN
33#ifndef NOMINMAX
34#   define NOMINMAX
35#endif
36#include <windows.h>
37#endif
38
39constexpr int HTTP_POLLING_SECONDS = 1;
40
41static common_speculative_output_limits server_output_limits(const common_params & params) {
42    if (params.embedding ||
43            (params.pooling_type != LLAMA_POOLING_TYPE_UNSPECIFIED && params.pooling_type != LLAMA_POOLING_TYPE_NONE)) {
44        return { params.n_batch, 1 };
45    }
46
47    auto result = common_speculative_get_output_limits(
48            params.n_batch, params.n_parallel, common_speculative_n_max(&params.speculative));
49
50    result.total   = std::max<int32_t>(1, result.total);
51    result.per_seq = std::max<int32_t>(1, result.per_seq);
52    return result;
53}
54
55// synthetic draft verification for benchmarking - accept draft tokens at random instead of by match with the target
56// on replay the draft was already accepted before a context checkpoint restore, so repeat the same decisions
57static std::vector<llama_token> server_sample_and_accept_synth(
58        common_sampler * smpl,
59        llama_context * ctx,
60        const std::vector<int32_t> & idxs,
61        const llama_tokens & draft,
62        const std::vector<double> & synth_probs,
63        std::mt19937 & rng,
64        bool is_replay) {
65    GGML_ASSERT(idxs.size() == draft.size() + 1);
66    GGML_ASSERT(synth_probs.size() >= draft.size());
67
68    std::vector<llama_token> result;
69    result.reserve(idxs.size());
70
71    const llama_vocab * vocab = llama_model_get_vocab(llama_get_model(ctx));
72    std::uniform_real_distribution<double> dist(0.0, 1.0);
73    for (size_t i = 0; i < draft.size(); ++i) {
74        const llama_token id = common_sampler_sample(smpl, ctx, idxs[i]);
75        const bool accept = is_replay || dist(rng) < synth_probs[i];
76        // do not accept a drafted EOG token - it would end the generation early
77        // on replay the last token is from the target and can be EOG, so skip this check
78        if (accept && (is_replay || !llama_vocab_is_eog(vocab, draft[i]))) {
79            // synthetic draft tokens do not advance grammar or reasoning state
80            // the last replay token is from the target and must advance both
81            const bool is_replay_target = is_replay && i + 1 == draft.size();
82            common_sampler_accept(smpl, draft[i], is_replay_target);
83            result.push_back(draft[i]);
84            continue;
85        }
86
87        common_sampler_accept(smpl, id, true);
88        result.push_back(id);
89        return result;
90    }
91
92    const llama_token id = common_sampler_sample(smpl, ctx, idxs[draft.size()]);
93    common_sampler_accept(smpl, id, true);
94    result.push_back(id);
95
96    return result;
97}
98
99// state diagram: https://github.com/ggml-org/llama.cpp/pull/9283
100enum slot_state {
101    SLOT_STATE_IDLE,
102    SLOT_STATE_WAIT_OTHER, // after assigning a task, but waiting for parent slot to process prompt
103    SLOT_STATE_STARTED,    // after assigning a task and about to process prompt
104    SLOT_STATE_PROCESSING_PROMPT,
105    SLOT_STATE_DONE_PROMPT,
106    SLOT_STATE_GENERATING,
107};
108
109struct server_slot; // forward declaration
110
111struct server_batch {
112    llama_batch batch;
113    bool batch_rendered = false;
114
115    struct token {
116        int32_t id_slot;
117        llama_token token;
118        llama_pos pos;
119        bool output;
120        bool is_prompt; // for stats tracking
121    };
122    std::vector<token> tokens;
123    int32_t n_tokens_alloc = 0;
124    int32_t n_embd = 0;
125
126    // track if given slot can be batched with slots already in the batch
127    server_slot * slot_batched = nullptr;
128
129    // in embd mode, we temporarily swap out the tokens arr and restore it on clear()
130    bool has_embd = false;
131    llama_token * tokens_ptr = nullptr;
132    std::vector<float> embd;
133
134    float  alora_scale       = -1.0f;
135    size_t alora_disabled_id = 0;
136
137    server_batch() {
138        batch.pos = nullptr; // sentinel: uninitialized batch
139    }
140
141    ~server_batch() {
142        if (batch.pos != nullptr) {
143            clear();
144            llama_batch_free(batch);
145        }
146    }
147
148    void init(int32_t n_tokens_alloc, int32_t n_embd) {
149        this->n_tokens_alloc = n_tokens_alloc;
150        this->n_embd = n_embd;
151        batch = llama_batch_init(n_tokens_alloc, 0, 1);
152        tokens_ptr = batch.token;
153        tokens.reserve(n_tokens_alloc);
154    }
155
156    bool add(int32_t id_slot, llama_token token, llama_pos pos, bool output, bool is_prompt) {
157        GGML_ASSERT(!has_embd); // cannot mix tokens + embd in same batch
158        GGML_ASSERT(batch.pos != nullptr);
159        if ((int32_t)tokens.size() >= n_tokens_alloc) {
160            return false;
161        }
162        tokens.push_back({ id_slot, token, pos, output, is_prompt });
163        return true;
164    }
165
166    bool add(int32_t id_slot, const std::vector<float> & embd_in, llama_pos pos, bool output, bool is_prompt) {
167        GGML_ASSERT(batch.pos != nullptr);
168        if ((int32_t)tokens.size() >= n_tokens_alloc) {
169            return false;
170        }
171        tokens.push_back({ id_slot, LLAMA_TOKEN_NULL, pos, output, is_prompt });
172        has_embd = true;
173        embd.insert(embd.end(), embd_in.begin(), embd_in.end());
174        return true;
175    }
176
177    void clear() {
178        tokens.clear();
179        embd.clear();
180        common_batch_clear(batch);
181        slot_batched      = nullptr;
182        alora_scale       = -1.0f;
183        alora_disabled_id = 0;
184        batch_rendered    = false;
185        has_embd          = false;
186        if (batch.token == nullptr) {
187            batch.token = tokens_ptr;
188            batch.embd  = nullptr;
189        }
190    }
191
192    int32_t size() const {
193        return (int32_t)tokens.size();
194    }
195
196    void set_output(int32_t idx, bool output) {
197        GGML_ASSERT(idx >= 0 && idx < (int32_t)tokens.size());
198        tokens[idx].output = output;
199    }
200
201    void render() {
202        GGML_ASSERT(!batch_rendered);
203        GGML_ASSERT(batch.pos != nullptr);
204        common_batch_clear(batch);
205        for (int32_t i = 0; i < size(); i++) {
206            const auto & t = tokens[i];
207            common_batch_add(batch, t.token, t.pos, { t.id_slot }, t.output);
208        }
209        if (has_embd) {
210            batch.token = nullptr; // will be restored on clear()
211            batch.embd  = embd.data();
212        }
213        batch_rendered = true;
214    }
215
216    llama_batch get_view(int32_t off, int32_t n_tokens) const {
217        GGML_ASSERT(batch.pos != nullptr);
218        GGML_ASSERT(batch_rendered);
219        GGML_ASSERT(off >= 0 && off < size());
220        GGML_ASSERT(n_tokens > 0 && off + n_tokens <= size());
221
222        auto * token = batch.token ? batch.token + off          : nullptr;
223        auto * embd  = batch.embd  ? batch.embd  + off * n_embd : nullptr;
224
225        llama_batch view = {
226            n_tokens,
227            token,
228            embd,
229            batch.pos      + off,
230            batch.n_seq_id + off,
231            batch.seq_id   + off,
232            batch.logits   + off,
233        };
234
235        return view;
236    }
237};
238
239struct server_slot {
240    int id;
241
242    llama_context * ctx_tgt = nullptr;
243    llama_context * ctx_dft = nullptr;
244
245    common_memory mem;
246
247    // multimodal
248    mtmd_context * mctx = nullptr;
249    mtmd::batch_ptr mbatch = nullptr;
250
251    // speculative decoding
252    common_speculative * spec;
253
254    llama_tokens spec_draft;
255    llama_tokens spec_prompt;
256    std::vector<int32_t> spec_i_batch;
257    common_prompt_checkpoint spec_ckpt;
258    bool spec_is_replay = false;
259    std::mt19937 spec_synth_rng;
260
261    // TODO: move members that belong to the task (such as `generated_text`, `has_new_line`) to task_results_state
262    //       see https://github.com/ggml-org/llama.cpp/pull/18283#issuecomment-3710175837
263    std::unique_ptr<const server_task> task;
264    std::unique_ptr<const server_task> task_prev; // used for debugging
265
266    // used to determine the slot that has been used the longest
267    int64_t t_last_used = -1;
268
269    // generation props
270    int32_t n_ctx   = 0;  // context size per slot
271    int32_t n_keep  = 0;
272    int32_t i_batch = -1;
273
274    // effective generation limit for the current task, -1 means unlimited
275    int32_t n_predict_max = -1;
276
277    size_t last_nl_pos = 0;
278
279    std::string  generated_text;
280    std::string  debug_generated_text;
281    llama_tokens generated_tokens;
282    size_t n_sent_text = 0; // number of sent text character (i.e. handle partial UTF-8 on streaming)
283
284    std::vector<completion_token_output> generated_token_probs;
285
286    bool has_next_token = true;
287    bool has_new_line   = false;
288    bool truncated      = false;
289
290    stop_type stop;
291
292    std::string stopping_word;
293
294    // state
295    slot_state state = SLOT_STATE_IDLE;
296
297    server_prompt prompt;
298
299    bool prompt_save(server_prompt_cache & prompt_cache) const {
300        if (prompt.tokens.size() == 0) {
301            return false;
302        }
303
304        const size_t cur_size_tgt =           llama_state_seq_get_size_ext(ctx_tgt, id, LLAMA_STATE_SEQ_FLAGS_NONE);
305        const size_t cur_size_dft = ctx_dft ? llama_state_seq_get_size_ext(ctx_dft, id, LLAMA_STATE_SEQ_FLAGS_NONE) : 0;
306
307        const size_t cur_size = cur_size_tgt + cur_size_dft;
308
309        SRV_TRC(" - saving prompt with length %d, total state size = %.3f MiB (draft: %.3f MiB)\n",
310                (int) prompt.tokens.size(), cur_size / (1024.0 * 1024.0), cur_size_dft / (1024.0 * 1024.0));
311
312        auto * cur = prompt_cache.alloc(prompt, cur_size_tgt, cur_size_dft);
313        if (cur == nullptr) {
314            return false;
315        }
316
317        llama_state_seq_get_data_ext(ctx_tgt, cur->data.main.data(), cur_size_tgt, id, LLAMA_STATE_SEQ_FLAGS_NONE);
318        if (ctx_dft) {
319            llama_state_seq_get_data_ext(ctx_dft, cur->data.drft.data(), cur_size_dft, id, LLAMA_STATE_SEQ_FLAGS_NONE);
320        }
321
322        return true;
323    }
324
325    bool prompt_load(server_prompt_cache & prompt_cache, const server_tokens & tokens) {
326        bool res = prompt_cache.load(prompt, tokens, ctx_tgt, ctx_dft, id);
327        if (!res) {
328            SLT_WRN(*this, "%s", "failed to load prompt from cache\n");
329        }
330
331        return res;
332    }
333
334    void prompt_clear() {
335        SLT_TRC(*this, "clearing prompt with %zu tokens\n", prompt.tokens.size());
336
337        mem.seq_rm(id, -1, -1);
338
339        prompt.clear();
340    }
341
342    std::vector<common_adapter_lora_info> lora;
343    int32_t alora_invocation_start = -1;
344
345    // sampling
346    json json_schema;
347
348    common_sampler_ptr smpl;
349
350    llama_token sampled; // in speculative mode, this is the last accepted token
351
352    // for TTS models, this is the embd generated from prev step, decode this to generate next hidden state
353    // corresponding to one token position (size = n_embd)
354    std::vector<float> inp_embd;
355
356    server_slot_stats stats;
357
358    // accepted tokens per draft position
359    // not in server_slot_stats to avoid copying to every task result
360    std::vector<uint64_t> n_accepted_per_pos;
361
362    std::function<void(int /* id_slot */)>   callback_on_release;
363    std::function<void(const server_slot &)> callback_on_reset; // called before reset()
364
365    // this is for printing timings with slot progress, not part of metrics
366    int64_t t_print_last = 0;
367    int32_t n_gen_last = 0;
368
369    void reset() {
370        SLT_DBG(*this, "%s", "\n");
371
372        spec_is_replay = false;
373
374        last_nl_pos    = 0;
375        generated_text = "";
376        has_new_line   = false;
377        truncated      = false;
378        stop           = STOP_TYPE_NONE;
379        stopping_word  = "";
380        n_sent_text    = 0;
381
382        if (can_speculate()) {
383            spec_draft.clear();
384            spec_i_batch.clear();
385            spec_ckpt.clear();
386        }
387        generated_tokens.clear();
388        generated_token_probs.clear();
389        json_schema = json();
390
391        task_prev = std::move(task);
392        task.reset();
393
394        // note: callback_on_reset() must have run before this, see release()
395        stats = {};
396        n_accepted_per_pos.clear();
397
398        n_predict_max = -1;
399
400        llama_set_sampler(ctx_tgt, id, nullptr);
401
402        // clear alora start
403        alora_invocation_start = -1;
404
405        // clear multimodal state
406        mbatch.reset();
407    }
408
409    void init_sampler() const {
410        common_sampler_reset(smpl.get());
411
412        if (!task->need_sampling()) {
413            return;
414        }
415
416        const int64_t t_start = ggml_time_us();
417
418        int n_text = 0;
419
420        for (int i = 0; i < (int) prompt.tokens.size(); i++) {
421            const llama_token id = prompt.tokens[i];
422
423            if (id != LLAMA_TOKEN_NULL) {
424                common_sampler_accept(smpl.get(), id, false);
425                n_text++;
426            }
427        }
428
429        SLT_TRC(*this, "init sampler, took %0.2f ms, tokens: text = %d, total = %d\n",
430                (ggml_time_us() - t_start) / 1000.0, n_text, (int) prompt.tokens.size());
431    }
432
433    bool need_embd() const {
434        GGML_ASSERT(task);
435        return task->need_embd();
436    }
437
438    // if the context does not have a memory module then all embeddings have to be computed within a single ubatch
439    // also we cannot split if the pooling would require any past tokens
440    // (MTP supports splitting — uses task->need_embd() not need_embd())
441    bool can_split() const {
442        GGML_ASSERT(task);
443
444        if (llama_model_has_encoder(llama_get_model(ctx_tgt)) && llama_model_has_decoder(llama_get_model(ctx_tgt))) {
445            return false;
446        }
447
448        return
449            !task->need_embd() ||
450            (llama_get_memory(ctx_tgt) && llama_pooling_type(ctx_tgt) == LLAMA_POOLING_TYPE_LAST);
451    }
452
453    bool can_batch_with(server_slot & other_slot) const {
454        GGML_ASSERT(task);
455
456        return task->type == other_slot.task->type
457            && inp_embd.size() == other_slot.inp_embd.size()
458            && are_lora_equal(lora, other_slot.lora);
459    }
460
461    // returns -1 if the generation is limitless
462    int32_t n_remaining() const {
463        return n_predict_max == -1 ? -1 : n_predict_max - (int32_t) stats.n_gen;
464    }
465
466    bool has_budget() const {
467        return n_predict_max == -1 || n_remaining() > 0;
468    }
469
470    bool is_processing() const {
471        return state != SLOT_STATE_IDLE;
472    }
473
474    bool can_speculate() const {
475        return !!spec;
476    }
477
478    void add_token(const completion_token_output & token) {
479        if (!is_processing()) {
480            SLT_WRN(*this, "%s", "slot is not processing\n");
481            return;
482        }
483
484        generated_token_probs.push_back(token);
485    }
486
487    int get_n_draft_max() const {
488        GGML_ASSERT(task);
489
490        if (!can_speculate()) {
491            return 0;
492        }
493
494        // determine the max draft that fits the current slot state
495        // note: slot.prompt is not yet expanded with the `id` token sampled above
496        //       also, need to leave space for 1 extra token to allow context shifts
497        int n_draft_max = n_ctx - prompt.n_tokens() - 2;
498
499        if (n_remaining() > 0) {
500            n_draft_max = std::min(n_draft_max, n_remaining() - 1);
501        }
502
503        SLT_DBG(*this, "max possible draft: %d\n", n_draft_max);
504
505        return n_draft_max;
506    }
507
508    // add sampled token of this slot to the batch, optionally add the speculative draft tokens if any
509    void handle_last_sampled_token(server_batch & batch) {
510        bool add_ok = true;
511        if (spec_draft.empty()) {
512            // no speculative decoding
513            i_batch = batch.size();
514
515            if (!inp_embd.empty()) {
516                add_ok &= batch.add(id, inp_embd, prompt.tokens.pos_next(), true, false);
517            } else {
518                add_ok &= batch.add(id, sampled, prompt.tokens.pos_next(), true, false);
519            }
520
521            SLT_DBG(*this, "slot decode token, id=%d, n_ctx = %d, n_tokens = %d, truncated = %d\n",
522                    sampled, n_ctx, prompt.n_tokens(), truncated);
523        } else {
524            SLT_DBG(*this, "generate_draft: id=%d, #tokens=%zu, #draft=%zu, pos_next=%d\n",
525                    sampled, prompt.tokens.size(), spec_draft.size(), prompt.tokens.pos_next());
526
527            GGML_ASSERT(spec_i_batch.empty());
528
529            spec_i_batch.push_back(batch.size());
530            for (size_t i = 0; i < spec_draft.size(); i++) {
531                spec_i_batch.push_back(batch.size() + i + 1);
532            }
533
534            auto pos0 = prompt.tokens.pos_next();
535
536            add_ok &= batch.add(id, sampled, pos0++, true, false);
537            for (auto token : spec_draft) {
538                add_ok &= batch.add(this->id, token, pos0++, true, false);
539            }
540        }
541
542        GGML_ASSERT(add_ok && "batch must be large enough to hold the sampled and draft tokens");
543
544        prompt.tokens.push_back(sampled);
545        prompt.tokens.insert(spec_draft);
546    }
547
548    void release() {
549        if (is_processing()) {
550            GGML_ASSERT(task);
551
552            SLT_INF(*this, "stop processing: n_tokens = %d, truncated = %d\n", prompt.n_tokens(), truncated);
553
554            t_last_used = ggml_time_us();
555
556            state = SLOT_STATE_IDLE;
557
558            // do not keep context of the child slots - the parent's context is enough
559            if (task->is_child()) {
560                prompt_clear();
561            }
562
563            callback_on_reset(*this);
564
565            reset();
566
567            callback_on_release(id);
568        }
569    }
570
571    size_t find_stopping_strings(const std::string & text, const size_t last_token_size, bool is_full_stop) {
572        GGML_ASSERT(task);
573
574        size_t stop_pos = std::string::npos;
575
576        for (const std::string & word : task->params.antiprompt) {
577            size_t pos;
578
579            if (is_full_stop) {
580                const size_t tmp      = word.size() + last_token_size;
581                const size_t from_pos = text.size() > tmp ? text.size() - tmp : 0;
582
583                pos = text.find(word, from_pos);
584            } else {
585                // otherwise, partial stop
586                pos = string_find_partial_stop(text, word);
587            }
588
589            if (pos != std::string::npos && (stop_pos == std::string::npos || pos < stop_pos)) {
590                if (is_full_stop) {
591                    stop           = STOP_TYPE_WORD;
592                    stopping_word  = word;
593                    has_next_token = false;
594                }
595                stop_pos = pos;
596            }
597        }
598
599        return stop_pos;
600    }
601
602    void print_timings_tg() {
603        if (stats.n_gen < 100) {
604            return;
605        }
606
607        const int64_t t_now = ggml_time_us();
608
609        if (t_now - t_print_last < 3*1000*1000) {
610            return;
611        }
612
613        const double n_gen_second     = stats.n_gen_tps();
614        const double n_gen_second_win = 1e6 / (t_now - t_print_last) * (stats.n_gen - n_gen_last);
615
616        t_print_last = t_now;
617        n_gen_last = stats.n_gen;
618
619        SLT_INF(*this, "n_gen = %6d, tg = %6.2f t/s, tg_3s = %6.2f t/s\n", (int) stats.n_gen, n_gen_second, n_gen_second_win);
620    }
621
622    void print_timings_pp() const {
623        const double t_prompt_total = stats.t_prompt_ms();
624
625        if (t_prompt_total < 3000.0) {
626            return;
627        }
628
629        const double n_prompt_second = stats.n_prompt_tps();
630        const double f_progress = task->n_tokens() > 0 ? (double) prompt.n_tokens() / task->n_tokens() : 0.0;
631
632        SLT_INF(*this, "prompt processing, n_tokens = %6d, progress = %.2f, t = %6.2f s / %.2f tokens per second\n",
633                (int) stats.n_prompt_processed, f_progress, t_prompt_total / 1e3, n_prompt_second);
634    }
635
636    void print_timings() const {
637        const double t_prompt_total = stats.t_prompt_ms();
638        const double t_gen_total    = stats.t_gen_ms();
639
640        const double t_prompt        = stats.t_prompt_per_token_ms();
641        const double n_prompt_second = stats.n_prompt_tps();
642
643        const double t_gen        = stats.t_gen_per_token_ms();
644        const double n_gen_second = stats.n_gen_tps();
645
646        SLT_INF(*this,
647                "prompt eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second)\n",
648                t_prompt_total, (int) stats.n_prompt_processed, t_prompt, n_prompt_second);
649
650        SLT_INF(*this,
651                "       eval time = %10.2f ms / %5d tokens (%8.2f ms per token, %8.2f tokens per second)\n",
652                t_gen_total, (int) stats.n_gen, t_gen, n_gen_second);
653
654        SLT_INF(*this,
655                "      total time = %10.2f ms / %5d tokens\n",
656                t_prompt_total + t_gen_total, (int) (stats.n_prompt_processed + stats.n_gen));
657
658        SLT_INF(*this,
659                "   graphs reused = %10d\n",
660                llama_perf_context(ctx_tgt).n_reused);
661
662        const int32_t n_draft_total       = stats.n_draft_tokens;
663        const int32_t n_draft_accepted    = stats.n_draft_accepted;
664        const int32_t n_draft_verif_steps = stats.n_draft_verif_steps;
665
666        if (n_draft_total > 0) {
667            const float  draft_ratio  = (float) n_draft_accepted / n_draft_total;
668            const double mean_acc_len = n_draft_verif_steps > 0 ? 1.0 + (double) n_draft_accepted / (double) n_draft_verif_steps : 1.0;
669
670            std::string acceptance_rates_per_pos;
671            if (n_draft_verif_steps > 0) {
672                for (size_t i = 0; i < n_accepted_per_pos.size(); ++i) {
673                    if (i > 0) {
674                        acceptance_rates_per_pos += ", ";
675                    }
676                    acceptance_rates_per_pos += string_format("%.3f", (double) n_accepted_per_pos[i] / (double) n_draft_verif_steps);
677                }
678            }
679
680            SLT_INF(*this,
681                    "draft acceptance = %0.5f (%5d accepted / %5d generated), mean len = %5.2f\n",
682                    draft_ratio, n_draft_accepted, n_draft_total, mean_acc_len);
683            SLT_TRC(*this,
684                    "     acc per pos = (%s)\n", acceptance_rates_per_pos.c_str());
685        }
686
687        common_speculative_print_stats(spec);
688    }
689
690    json to_json(bool only_metrics = false) const {
691        json res;
692
693        res = {
694            {"id",            id},
695            {"n_ctx",         n_ctx},
696            {"speculative",   can_speculate()},
697            {"is_processing", is_processing()},
698        };
699
700        const auto & ptask = task ? task : task_prev;
701
702        if (ptask) {
703            res["id_task"] = ptask->id;
704            res["n_prompt_tokens"]           = (int32_t) prompt.tokens.size();
705            res["n_prompt_tokens_processed"] = stats.n_prompt_processed;
706            res["n_prompt_tokens_cache"]     = stats.n_prompt_cached;
707            res["params"] = ptask->params.to_json(only_metrics);
708            res["next_token"] = json::array({
709                {
710                    {"has_next_token", has_next_token},
711                    {"has_new_line",   has_new_line},
712                    {"n_remain",       n_remaining()},
713                    {"n_decoded",      stats.n_gen},
714                }
715            });
716
717            if (!only_metrics) {
718                res["prompt"] = ptask->tokens.detokenize(ctx_tgt, true);
719                res["generated"] = generated_text.empty() ? debug_generated_text : generated_text;
720            }
721        }
722
723        return res;
724    }
725
726    void copy_state_to(server_slot & other) const {
727        GGML_ASSERT(state == SLOT_STATE_DONE_PROMPT);
728
729        mem.seq_rm(other.id,     -1, -1);
730        mem.seq_cp(id, other.id, -1, -1);
731
732        other.i_batch = i_batch;
733
734        other.stats = stats;
735
736        other.prompt = prompt.clone();
737        other.init_sampler();
738    }
739};
740
741// returns 0 on success
742// caller need to update prompt.tokens after a successful call to keep track of the processing progress
743// note: this is not a member of server_slot because we want to run it inside yield_to_queue
744//       slot is passed as const to avoid accidental modification of the slot state
745//       some pointers are allowed to be used, they are not used by to_json()
746static int process_mtmd_chunk(const server_slot & slot, mtmd::batch_ptr & mbatch, size_t idx, size_t & n_tokens_out) {
747    GGML_ASSERT(slot.mctx);
748    const auto & mctx = slot.mctx;
749    const auto & input_tokens = slot.task->tokens;
750    const auto & chunk = input_tokens.find_chunk(idx);
751    int32_t res = 0;
752
753    auto try_decode = [&]() -> int32_t {
754        if (mbatch) {
755            float * embd = mtmd_batch_get_output_embd(mbatch.get(), chunk.get());
756            if (embd) {
757                void * cb_data = slot.spec;
758                static auto cb = [](llama_batch batch, void * user_data) {
759                    common_speculative * spec = static_cast<common_speculative *>(user_data);
760                    if (!common_speculative_process(spec, batch)) {
761                        return 1;
762                    }
763                    return 0;
764                };
765
766                llama_pos new_n_past; // unused for now
767                res = mtmd_helper_decode_image_chunk(
768                    mctx,
769                    slot.ctx_tgt,
770                    chunk.get(),
771                    embd,
772                    slot.prompt.tokens.pos_next(),
773                    slot.id,
774                    llama_n_batch(slot.ctx_tgt),
775                    &new_n_past,
776                    cb,
777                    cb_data
778                );
779                if (res != 0) {
780                    SLT_ERR(slot, "failed to decode mtmd chunk, idx = %zu, res = %d\n", idx, res);
781                    return -1;
782                }
783                n_tokens_out = mtmd_input_chunk_get_n_tokens(chunk.get());
784                return 0; // success
785            }
786        }
787        return 1; // (non-error) need to create & encode batch
788    };
789
790    // if the batch is already exist, try searching & encode
791    res = try_decode();
792    if (res == 0) {
793        return 0;
794    }
795    if (res < 0) {
796        // fatal error
797        return res;
798    }
799
800    // otherwise, the batch is either uninitialized or is used up
801    // we need to create & encode a new batch
802    mbatch.reset(mtmd_batch_init(mctx));
803    res = mtmd_batch_add_chunk(mbatch.get(), chunk.get());
804    GGML_ASSERT(res == 0); // we should never have an empty batch
805
806    // try batching as much as possible
807    int n_added = 1;
808    size_t idx_cur = idx;
809    while (res == 0) {
810        auto [next_chunk, next_idx] = input_tokens.find_next_media_chunk(idx_cur);
811        if (next_chunk == nullptr) {
812            break;
813        }
814        res = mtmd_batch_add_chunk(mbatch.get(), next_chunk->get());
815        n_added += (res == 0 ? 1 : 0);
816        idx_cur = next_idx;
817        SLT_DBG(slot, "try adding media chunk idx = %zu to batch, res = %d\n", next_idx, res);
818        // if res != 0, batch is full or chunk is not compatible -> this loop breaks
819    }
820
821    // TODO @ngxson : move this log line to debug when it become more stable
822    SLT_TRC(slot, "encoding mtmd batch from idx = %zu, n_chunks = %d\n", idx, n_added);
823
824    res = mtmd_batch_encode(mbatch.get());
825    if (res != 0) {
826        SLT_ERR(slot, "failed to encode mtmd batch for chunk idx = %zu, res = %d\n", idx, res);
827        return -1;
828    }
829
830    return try_decode();
831}
832
833//
834// server_context_impl (private implementation)
835//
836
837struct server_context_impl {
838    friend struct server_context;
839
840public:
841    // only use these pointers outside of this class:
842    //  - when not in sleeping state
843    //  - and, with thread-safe APIs (e.g., tokenizer calls)
844    llama_model * model_tgt = nullptr;
845
846    mtmd_context * mctx = nullptr;
847    // note: video_params.ffmpeg_bin_dir points into params_base, which outlives this struct
848    mtmd_helper_init_opt init_opt = mtmd_helper_init_opt_default();
849    const llama_vocab * vocab = nullptr;
850
851    server_queue    queue_tasks;
852    server_response queue_results;
853
854    // note: chat_params must not be refreshed upon existing sleeping state
855    server_chat_params chat_params;
856
857    server_state_callback_t callback_state = [](server_state, json) -> void {};
858
859    server_context_impl() {
860        mtmd_helper_log_set(common_log_default_callback, nullptr);
861    }
862
863    ~server_context_impl() {
864        if (!sleeping) {
865            // destroy() is already called when entering sleeping state
866            // we don't call it again here to avoid double free
867            destroy();
868        }
869    }
870
871    server_metrics get_metrics() const {
872        return metrics;
873    }
874
875    void reset_metrics_bucket() {
876        metrics.reset_bucket();
877    }
878
879private:
880    // note: accessing these fields outside of this class is not thread-safe
881    // use server_context methods instead
882
883    common_params params_base;
884
885    // note: keep these alive - they determine the lifetime of the model, context, etc.
886    common_init_result_ptr llama_init;
887
888    llama_context * ctx_tgt = nullptr;
889
890    server_batch batch;
891
892    llama_model   * model_dft = nullptr;
893    llama_context * ctx_dft   = nullptr;
894
895    common_speculative_init_result_ptr spec_init;
896
897    common_context_seq_rm_type ctx_tgt_seq_rm_type = COMMON_CONTEXT_SEQ_RM_TYPE_NO;
898    common_context_seq_rm_type ctx_dft_seq_rm_type = COMMON_CONTEXT_SEQ_RM_TYPE_NO;
899
900    common_speculative_ptr spec;
901
902    bool add_bos_token = true;
903    bool encoder_decoder = false;
904    llama_tokens decoder_prefix;
905
906    int32_t n_ctx; // total context for all clients / slots
907
908    // set to llama_model_n_swa(model)
909    // if swa_full is enabled, this is set to 0 to simulate a non-SWA model
910    int32_t n_swa;
911
912    // slots / clients
913    std::vector<server_slot> slots;
914
915    int trace = 0;        // env: LLAMA_TRACE
916    int slots_debug = 0;  // env: LLAMA_SERVER_SLOTS_DEBUG
917    int slots_n_diff = 0; // env: LLAMA_SERVER_SLOTS_N_DIFF
918
919    int n_empty_consecutive = 0;
920
921    std::unique_ptr<server_prompt_cache> prompt_cache;
922
923    server_metrics metrics;
924
925    // queued prompt stats - llama_decode() is async, so the timing is only valid after a sync
926    // note: kept out of server_metrics, which is copied as-is into the task result
927    int64_t  t_decode_start  = 0; // start of the last submitted decode
928    int64_t  t_prompt_start  = 0; // start of the oldest queued prompt decode
929    uint64_t n_prompt_queued = 0;
930
931    json json_ui_settings = json::object();
932
933    // Necessary similarity of prompt for slot selection
934    float slot_prompt_similarity = 0.0f;
935
936    std::string model_name; // name of the loaded model, to be used by API
937    std::set<std::string> model_aliases; // additional names for the model
938    std::set<std::string> model_tags;    // informational tags
939
940    bool sleeping = false;
941
942    int64_t t_last_load_progress_ms = 0;
943
944    void destroy() {
945        spec.reset();
946        spec_init.reset();
947
948        ctx_dft   = nullptr;
949        model_dft = nullptr;
950
951        llama_init.reset();
952
953        ctx_tgt = nullptr;
954        model_tgt = nullptr;
955
956        mtmd_free(mctx);
957        mctx = nullptr;
958    }
959
960    void handle_sleeping_state(bool new_state) {
961        GGML_ASSERT(sleeping != new_state);
962        if (new_state) {
963            if (callback_state) {
964                callback_state(SERVER_STATE_SLEEPING, {});
965                // note: for sleeping == false, event is emitted by load_model()
966            }
967            SRV_INF("%s", "server is entering sleeping state\n");
968            destroy();
969        } else {
970            SRV_INF("%s", "server is exiting sleeping state\n");
971            if (!load_model(params_base)) {
972                GGML_ABORT("failed to reload model after sleeping");
973            }
974        }
975        sleeping = new_state;
976    }
977
978    struct load_progress_data {
979        server_context_impl * ctx;
980        std::string stage;
981        std::vector<std::string> stages;
982        int64_t t_last_load_progress_ms = 0;
983        load_progress_data(server_context_impl * ctx, const std::string & stage) : ctx(ctx), stage(stage) {}
984    };
985    static bool load_progress_callback(float progress, void * user_data) {
986        auto * d = static_cast<load_progress_data *>(user_data);
987        GGML_ASSERT(d);
988        // always emit the first and final sample; throttle the rest to one per 200ms
989        {
990            auto & t_last = d->t_last_load_progress_ms;
991            const int64_t t_now = ggml_time_ms();
992            const bool first = t_last == 0;
993            const bool done  = progress >= 1.0f;
994            const bool throttled = !first && !done && (t_now - t_last) < 200;
995            if (throttled) {
996                return true;
997            }
998            t_last = t_now;
999        }
1000        if (d->ctx->callback_state) {
1001            d->ctx->callback_state(SERVER_STATE_LOADING, {
1002                {"stages", d->stages},
1003                {"current", d->stage},
1004                {"value", progress},
1005            });
1006        }
1007        return true;
1008    }
1009
1010    // load the model and initialize llama_context
1011    // this may also be called to resume from sleeping state
1012    bool load_model(common_params & params) {
1013        load_progress_data load_progress_text  (this, "text_model");
1014        load_progress_data load_progress_mmproj(this, "mmproj_model");
1015        load_progress_data load_progress_spec  (this, "spec_model");
1016
1017        const bool is_resume = sleeping;
1018
1019        params_base = params;
1020        const auto output_limits = server_output_limits(params_base);
1021        params_base.n_outputs_max = output_limits.total;
1022        params_base.n_outputs_max_per_seq = output_limits.per_seq;
1023
1024        const bool has_mmproj = !params.mmproj.path.empty();
1025        const bool has_draft = params.speculative.has_dft();
1026        const bool spec_mtp = std::find(params_base.speculative.types.begin(),
1027                                        params_base.speculative.types.end(),
1028                                        COMMON_SPECULATIVE_TYPE_DRAFT_MTP) != params_base.speculative.types.end();
1029        const bool has_spec = has_draft || spec_mtp;
1030
1031        if (callback_state) {
1032            std::vector<std::string> stages = {"text_model"};
1033            if (has_spec) {
1034                stages.push_back("spec_model");
1035            }
1036            if (has_mmproj) {
1037                stages.push_back("mmproj_model");
1038            }
1039            load_progress_text.stages   = stages;
1040            load_progress_mmproj.stages = stages;
1041            load_progress_spec.stages   = stages;
1042
1043            // trigger 0% progress
1044            load_progress_callback(0.0f, &load_progress_text);
1045        }
1046
1047
1048        SRV_INF("loading model '%s'\n", params.model.get_name().c_str());
1049        SRV_TRC("local path '%s'\n", params.model.path.c_str());
1050
1051        std::string & mmproj_path = params_base.mmproj.path;
1052        mtmd_context_params mparams = mtmd_context_params_default();
1053        if (has_mmproj) {
1054            mparams.use_gpu          = params_base.mmproj_use_gpu;
1055            mparams.device           = params_base.mmproj_device;
1056            mparams.print_timings    = false;
1057            mparams.n_threads        = params_base.cpuparams.n_threads;
1058            mparams.flash_attn_type  = params_base.flash_attn_type;
1059            mparams.warmup           = params_base.warmup;
1060            mparams.image_min_tokens = params_base.image_min_tokens;
1061            mparams.image_max_tokens = params_base.image_max_tokens;
1062            mparams.batch_max_tokens = params_base.mtmd_batch_max_tokens;
1063            mparams.media_marker     = get_media_marker();
1064            // progress callback
1065            mparams.progress_callback           = load_progress_callback;
1066            mparams.progress_callback_user_data = &load_progress_mmproj;
1067        }
1068
1069        // optionally get the memory usage of mmproj
1070        if (has_mmproj && params_base.fit_params) {
1071            int64_t t_start = ggml_time_us();
1072            auto mmproj_mem = mtmd_get_memory_usage(mmproj_path.c_str(), mparams);
1073            int64_t t_elapsed = ggml_time_us() - t_start;
1074            if (!mmproj_mem.empty()) {
1075                size_t total = 0;
1076                for (auto & [dev, size] : mmproj_mem) {
1077                    total += size;
1078                }
1079                SRV_TRC("[mtmd] estimated worst-case memory usage of mmproj is %.2f MiB (took %.2f ms)\n", total / (1024.0 * 1024.0), t_elapsed / 1000.0);
1080                GGML_ASSERT(!params_base.fit_params_target.empty());
1081                for (auto & [dev, size] : mmproj_mem) {
1082                    for (size_t i = 0; i < ggml_backend_dev_count(); i++) {
1083                        if (ggml_backend_dev_get(i) == dev) {
1084                            if (i < params_base.fit_params_target.size()) {
1085                                SRV_DBG("[mtmd] adding %.2f MiB to fit_params_target for device %s\n", size / (1024.0 * 1024.0), ggml_backend_dev_name(dev));
1086                                params_base.fit_params_target[i] += size;
1087                            }
1088                            break;
1089                        }
1090                    }
1091                }
1092            } else {
1093                SRV_ERR("%s", "[mtmd] failed to get memory usage of mmproj\n");
1094            }
1095        }
1096
1097        // note: the draft / MTP context is fitted together with the target model, see common_fit_extra_model
1098
1099        // attach a progress callback
1100        {
1101            params_base.load_progress_callback = load_progress_callback;
1102            params_base.load_progress_callback_user_data = &load_progress_text;
1103        }
1104
1105        llama_init = common_init_from_params(params_base);
1106
1107        model_tgt = llama_init->model();
1108        ctx_tgt   = llama_init->context();
1109
1110        if (model_tgt == nullptr) {
1111            SRV_ERR("failed to load model, '%s'\n", params_base.model.path.c_str());
1112            return false;
1113        }
1114
1115        if (ctx_tgt == nullptr) {
1116            SRV_ERR("failed to create_context with model '%s'\n", params_base.model.path.c_str());
1117            return false;
1118        }
1119
1120        vocab = llama_model_get_vocab(model_tgt);
1121
1122        n_ctx = llama_n_ctx(ctx_tgt);
1123
1124        add_bos_token = llama_vocab_get_add_bos(vocab);
1125        encoder_decoder = llama_model_has_encoder(model_tgt) && llama_model_has_decoder(model_tgt);
1126        if (encoder_decoder) {
1127            if (has_spec || has_mmproj || !params_base.lora_adapters.empty()) {
1128                SRV_ERR("%s", "encoder-decoder serving does not support speculative decoding, multimodal input or LoRA\n");
1129                return false;
1130            }
1131            llama_token decoder_start = llama_model_decoder_start_token(model_tgt);
1132            if (decoder_start == LLAMA_TOKEN_NULL) {
1133                decoder_start = llama_vocab_bos(vocab);
1134            }
1135            if (decoder_start < 0 || decoder_start >= llama_vocab_n_tokens(vocab)) {
1136                SRV_ERR("%s", "encoder-decoder model has no valid decoder start token\n");
1137                return false;
1138            }
1139            decoder_prefix = {decoder_start};
1140            char arch[64] = {};
1141            llama_model_meta_val_str(model_tgt, "general.architecture", arch, sizeof(arch));
1142            if (std::strcmp(arch, "aliceai_t5_moe") == 0) {
1143                // AliceAI's infill sentinel is decoder input, not response text.
1144                auto span = common_tokenize(vocab, "<SPAN#0>", false, true);
1145                if (span.size() != 1 || common_token_to_piece(vocab, span[0], true) != "<SPAN#0>") {
1146                    SRV_ERR("%s", "AliceAI requires a <SPAN#0> token\n");
1147                    return false;
1148                }
1149                decoder_prefix.push_back(span[0]);
1150            }
1151            // Encoder outputs belong to the context, not individual KV sequences.
1152            params_base.n_parallel = 1;
1153            params_base.ctx_shift = false;
1154            params_base.n_cache_reuse = 0;
1155            params_base.cache_ram_mib = 0;
1156            params_base.cache_idle_slots = false;
1157            params_base.n_ctx_checkpoints = 0;
1158            params_base.slot_save_path.clear();
1159            prompt_cache.reset();
1160            SRV_WRN("encoder-decoder mode: one slot, no prompt cache or context shift; encoder limit = %u tokens\n", llama_n_ubatch(ctx_tgt));
1161        }
1162
1163        if (has_spec) {
1164            // spec_mtp doesn't use load a model internally, so we report 0.0 and 1.0 manually
1165            load_progress_callback(0.0f, &load_progress_spec);
1166            load_progress_spec.t_last_load_progress_ms = 0;  // reset so internal cbs aren't delayed
1167
1168            {
1169                common_params params_dft = common_base_params_to_speculative(params_base);
1170
1171                // progress callback
1172                params_dft.load_progress_callback           = load_progress_callback;
1173                params_dft.load_progress_callback_user_data = &load_progress_spec;
1174
1175                spec_init = common_speculative_init_from_params(params_dft, model_tgt, ctx_tgt);
1176                model_dft = spec_init->model();
1177                ctx_dft   = spec_init->context();
1178
1179                if (has_draft && model_dft == nullptr) {
1180                    SRV_ERR("failed to load draft model, '%s'\n", params_dft.model.path.c_str());
1181                    return false;
1182                }
1183
1184                if (ctx_dft == nullptr) {
1185                    SRV_ERR("%s", "failed to create MTP context\n");
1186                    return false;
1187                }
1188
1189                params_base.speculative.draft.ctx_tgt = ctx_tgt;
1190                params_base.speculative.draft.ctx_dft = ctx_dft;
1191            }
1192
1193            load_progress_callback(1.0f, &load_progress_spec);
1194        }
1195
1196        if (has_mmproj) {
1197            if (callback_state) {
1198                callback_state(SERVER_STATE_LOADING, {{"stage", "mmproj_model"}});
1199            }
1200
1201            if (!is_resume) {
1202                mtmd_helper_log_set(common_log_default_callback, nullptr);
1203            }
1204
1205            mctx = mtmd_init_from_file(mmproj_path.c_str(), model_tgt, mparams);
1206            if (mctx == nullptr) {
1207                SRV_ERR("failed to load multimodal model, '%s'\n", mmproj_path.c_str());
1208                return false;
1209            }
1210            SRV_INF("loaded multimodal model, '%s'\n", mmproj_path.c_str());
1211
1212            init_opt.video_params.fps_target = params_base.video_fps;
1213            init_opt.video_params.timestamp_interval_ms = params_base.video_timestamp_interval_ms;
1214            init_opt.video_params.ffmpeg_bin_dir = params_base.video_ffmpeg_bin_dir.empty()
1215                                ? nullptr : params_base.video_ffmpeg_bin_dir.c_str();
1216
1217            if (params_base.ctx_shift) {
1218                params_base.ctx_shift = false;
1219                SRV_WRN("%s\n", "ctx_shift is not supported by multimodal, it will be disabled");
1220            }
1221
1222            if (params_base.n_cache_reuse) {
1223                params_base.n_cache_reuse = 0;
1224                SRV_WRN("%s\n", "cache_reuse is not supported by multimodal, it will be disabled");
1225            }
1226        }
1227
1228        if (!llama_memory_can_shift(llama_get_memory(ctx_tgt))) {
1229            if (params_base.ctx_shift) {
1230                params_base.ctx_shift = false;
1231                SRV_WRN("%s\n", "ctx_shift is not supported by this context, it will be disabled");
1232            }
1233
1234            if (params_base.n_cache_reuse) {
1235                params_base.n_cache_reuse = 0;
1236                SRV_WRN("%s\n", "cache_reuse is not supported by this context, it will be disabled");
1237            }
1238        }
1239
1240        if (llama_model_n_swa(model_tgt) == 0) {
1241            if (params_base.swa_full) {
1242                params_base.swa_full = false;
1243                SRV_WRN("%s\n", "swa_full is not supported by this model, it will be disabled");
1244            }
1245        }
1246
1247        n_swa = params_base.swa_full ? 0 : llama_model_n_swa(model_tgt);
1248
1249        // Necessary similarity of prompt for slot selection
1250        slot_prompt_similarity = params_base.slot_prompt_similarity;
1251
1252        const int n_ctx_train = llama_model_n_ctx_train(model_tgt);
1253
1254        {
1255            // note: the capping itself is done in n_ctx_slot(), here we only report it
1256            const int n_ctx_seq = llama_n_ctx_seq(ctx_tgt);
1257
1258            if (params_base.kv_unified_per_slot > 0) {
1259                if (n_ctx_seq > params_base.kv_unified_per_slot) {
1260                    SRV_INF("capping per-slot context (%d) to --kv-unified-per-slot (%d)\n",
1261                            n_ctx_seq, params_base.kv_unified_per_slot);
1262                } else if (params_base.kv_unified_per_slot > n_ctx_seq) {
1263                    // cap is above the per-slot pool capacity, so it can never bind
1264                    SRV_WRN(
1265                        "--kv-unified-per-slot (%d) exceeds the per-slot pool capacity (%d) - cap has no effect, "
1266                        "slots are limited to %d (raise the KV pool with -c, or unset -c to size it to "
1267                        "n_parallel * kv_unified_per_slot)\n",
1268                        params_base.kv_unified_per_slot, n_ctx_seq, n_ctx_seq);
1269                }
1270            }
1271
1272            const int n_ctx_capped = params_base.kv_unified_per_slot > 0 ?
1273                std::min(n_ctx_seq, params_base.kv_unified_per_slot) : n_ctx_seq;
1274
1275            if (n_ctx_capped > n_ctx_train) {
1276                SRV_WRN("the slot context (%d) exceeds the training context of the model (%d) - capping\n",
1277                        n_ctx_capped, n_ctx_train);
1278            }
1279        }
1280
1281        slots.clear();
1282
1283        ctx_tgt_seq_rm_type = encoder_decoder ? COMMON_CONTEXT_SEQ_RM_TYPE_NO : common_context_can_seq_rm(ctx_tgt);
1284        if (ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_NO) {
1285            SRV_WRN("%s", "speculative decoding not supported by this context\n");
1286        }
1287
1288        if (ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL) {
1289            SRV_TRC("%s", "speculative decoding will use checkpoints\n");
1290        }
1291
1292        // setup slots
1293        SRV_INF("initializing, n_slots = %d, n_ctx_slot = %d, kv_unified = '%s'\n",
1294                params_base.n_parallel, n_ctx_slot(), params_base.kv_unified ? "true" : "false");
1295
1296        // initialize slots
1297        for (int i = 0; i < params_base.n_parallel; i++) {
1298            slots.emplace_back();
1299        }
1300
1301        // try speculative decoding
1302        if (ctx_tgt_seq_rm_type != COMMON_CONTEXT_SEQ_RM_TYPE_NO) {
1303            try {
1304                spec.reset(common_speculative_init(params_base.speculative, params_base.n_parallel));
1305            } catch (const std::exception & e) {
1306                SRV_ERR("failed to initialize speculative decoding context: %s\n", e.what());
1307                if (params_base.speculative.has_synth()) {
1308                    return false;
1309                }
1310            }
1311        }
1312
1313        if (ctx_dft) {
1314            ctx_dft_seq_rm_type = common_context_can_seq_rm(ctx_dft);
1315        }
1316
1317        if (spec) {
1318            SRV_TRC("%s", "speculative decoding context initialized\n");
1319        } else {
1320            spec_init.reset();
1321            ctx_dft   = nullptr;
1322            model_dft = nullptr;
1323        }
1324
1325        if (!spec && params_base.speculative.has_synth()) {
1326            SRV_ERR("%s", "synthetic acceptance requires an initialized speculative decoding context\n");
1327            return false;
1328        }
1329
1330        for (int i = 0; i < params_base.n_parallel; i++) {
1331            server_slot & slot = slots[i];
1332
1333            slot.id      = i;
1334            slot.ctx_tgt = ctx_tgt;
1335            slot.ctx_dft = ctx_dft;
1336            slot.mem.init(ctx_tgt, ctx_dft);
1337            slot.spec    = spec.get();
1338            slot.n_ctx   = n_ctx_slot();
1339
1340            slot.mctx                   = mctx;
1341            slot.prompt.tokens.has_mtmd = mctx != nullptr;
1342
1343            SLT_TRC(slot, "new slot, n_ctx = %d\n", slot.n_ctx);
1344
1345            slot.callback_on_release = [this](int id_slot) {
1346                queue_tasks.pop_deferred_task(id_slot);
1347            };
1348
1349            slot.callback_on_reset = [this](const server_slot & slot) {
1350                // flush the generated token stats before reset()
1351                if (slot.stats.n_gen > 0) {
1352                    metrics_on_prediction(slot);
1353                }
1354            };
1355
1356            slot.reset();
1357        }
1358
1359        {
1360            const char * LLAMA_TRACE = getenv("LLAMA_TRACE");
1361            trace = LLAMA_TRACE ? atoi(LLAMA_TRACE) : 0;
1362
1363            if (trace) {
1364                SRV_WRN("LLAMA_TRACE = %d\n", trace);
1365            }
1366        }
1367
1368        {
1369            const char * LLAMA_SERVER_SLOTS_DEBUG = getenv("LLAMA_SERVER_SLOTS_DEBUG");
1370            slots_debug = LLAMA_SERVER_SLOTS_DEBUG ? atoi(LLAMA_SERVER_SLOTS_DEBUG) : 0;
1371
1372            if (slots_debug) {
1373                SRV_WRN("LLAMA_SERVER_SLOTS_DEBUG = %d\n", slots_debug);
1374            }
1375        }
1376
1377        {
1378            const char * LLAMA_SERVER_SLOTS_N_DIFF = getenv("LLAMA_SERVER_SLOTS_N_DIFF");
1379            slots_n_diff = LLAMA_SERVER_SLOTS_N_DIFF ? atoi(LLAMA_SERVER_SLOTS_N_DIFF) : 0;
1380
1381            if (slots_n_diff) {
1382                SRV_WRN("LLAMA_SERVER_SLOTS_N_DIFF = %d\n", slots_n_diff);
1383            }
1384        }
1385
1386        // the update_slots() logic will always submit a maximum of n_batch or n_parallel tokens
1387        // 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)
1388        {
1389            const int32_t n_batch = llama_n_batch(ctx_tgt);
1390            const int32_t n_embd  = llama_model_n_embd_inp(model_tgt);
1391            batch.init(std::max(n_batch, params_base.n_parallel), n_embd);
1392        }
1393
1394        if (params_base.cache_ram_mib != 0) {
1395            if (params_base.cache_ram_mib < 0) {
1396                SRV_TRC("prompt cache is enabled, size limit: %s\n", "no limit");
1397            } else {
1398                SRV_TRC("prompt cache is enabled, size limit: %d MiB\n", params_base.cache_ram_mib);
1399            }
1400            SRV_TRC("%s", "use `--cache-ram 0` to disable the prompt cache\n");
1401
1402            prompt_cache = std::make_unique<server_prompt_cache>(params_base.cache_ram_mib, n_ctx);
1403        } else {
1404            SRV_TRC("%s", "prompt cache is disabled - use `--cache-ram N` to enable it\n");
1405        }
1406        SRV_TRC("%s", "for more info see https://github.com/ggml-org/llama.cpp/pull/16391\n");
1407
1408        if (params_base.n_ctx_checkpoints > 0) {
1409            SRV_TRC("context checkpoints enabled, max = %d, min spacing = %d\n",
1410                    params_base.n_ctx_checkpoints, params_base.checkpoint_min_step);
1411        } else {
1412            SRV_TRC("%s", "context checkpoints disabled\n");
1413        }
1414
1415        if (!params_base.model_alias.empty()) {
1416            // backward compat: use first alias as model name
1417            model_name = *params_base.model_alias.begin();
1418        } else if (!params_base.model.get_name().empty()) {
1419            model_name = params_base.model.get_name();
1420        } else {
1421            // fallback: derive model name from file name
1422            auto model_path = std::filesystem::path(params_base.model.path);
1423            model_name = model_path.filename().string();
1424        }
1425
1426        model_aliases = params_base.model_alias;
1427        model_tags    = params_base.model_tags;
1428
1429        // propagate new defaults back to caller
1430        params = params_base;
1431
1432        if (!is_resume) {
1433            return init();
1434        }
1435
1436        if (callback_state) {
1437            callback_state(SERVER_STATE_READY, {});
1438        }
1439
1440        return true;
1441    }
1442
1443    // unlike load_model(), this is only called once during initialization
1444    bool init() {
1445        GGML_ASSERT(ctx_tgt   != nullptr);
1446        GGML_ASSERT(model_tgt != nullptr);
1447
1448        GGML_ASSERT(!sleeping);
1449
1450        // wiring up server queues
1451        queue_tasks.on_new_task([this](server_task && task, bool is_yielding) {
1452            return process_single_task(std::move(task), is_yielding);
1453        });
1454        queue_tasks.on_update_slots([this]() {
1455            update_slots();
1456        });
1457        queue_tasks.on_sleeping_state([this](bool sleeping) {
1458            handle_sleeping_state(sleeping);
1459        });
1460
1461        metrics.init();
1462
1463        if (params_base.cache_idle_slots) {
1464            if (params_base.cache_ram_mib == 0) {
1465                SRV_WRN("%s", "--cache-idle-slots requires --cache-ram, disabling\n");
1466                params_base.cache_idle_slots = false;
1467            } else {
1468                if (params_base.kv_unified) {
1469                    SRV_TRC("%s", "idle slots will be saved to prompt cache and cleared upon starting a new task\n");
1470                } else {
1471                    // without a unified KV cache, clearing a slot frees no reusable room, so we only
1472                    // publish a RAM-cache copy of idle slots (their KV stays in VRAM) [TAG_IDLE_SLOT_CLEAR]
1473                    SRV_TRC("%s", "idle slots will be saved to prompt cache upon starting a new task\n");
1474                }
1475                SRV_DBG("%s", "__TEST_TAG_CACHE_IDLE_SLOTS_ENABLED__\n");
1476            }
1477        }
1478
1479        {
1480            const std::string & cfg = params_base.ui_config_json;
1481            if (!cfg.empty()) {
1482                try {
1483                    json json_settings = json::parse(cfg);
1484                    json_ui_settings = json_settings;
1485                } catch (const std::exception & e) {
1486                    SRV_ERR("%s: failed to parse UI config: %s\n", __func__, e.what());
1487                    return false;
1488                }
1489            }
1490        }
1491
1492        // populate chat template params
1493        {
1494            common_chat_templates_ptr chat_templates;
1495            bool enable_thinking = false;
1496
1497            try {
1498                chat_templates = common_chat_templates_init(model_tgt, params_base.chat_template);
1499
1500                SRV_TRC("%s: chat template, example_format: '%s'\n", __func__,
1501                    common_chat_format_example(chat_templates.get(), params_base.use_jinja, params_base.default_template_kwargs).c_str());
1502
1503                // thinking is enabled if:
1504                // 1. It's not explicitly disabled via --reasoning off
1505                // 2. The chat template supports it
1506                const bool template_supports_thinking = params_base.use_jinja && common_chat_templates_support_enable_thinking(chat_templates.get());
1507                enable_thinking = params_base.enable_reasoning != 0 && template_supports_thinking;
1508                SRV_TRC("%s: chat template, thinking = %d\n", __func__, enable_thinking);
1509            } catch (const std::exception & e) {
1510                SRV_ERR("%s: chat template parsing error: %s\n", __func__, e.what());
1511                SRV_ERR("%s: please consider disabling jinja via --no-jinja, or use a custom chat template via --chat-template\n", __func__);
1512                SRV_ERR("%s: for example: --no-jinja --chat-template chatml\n", __func__);
1513                return false;
1514            }
1515
1516            // IMPORTANT: chat_params is reused across sleeping / resuming states,
1517            //            never store llama_context/llama_model pointers in chat_params,
1518            //            as they may be invalidated after sleeping
1519            chat_params = {
1520                /* use_jinja             */ params_base.use_jinja,
1521                /* prefill_assistant     */ params_base.prefill_assistant,
1522                /* reasoning_format      */ params_base.reasoning_format,
1523                /* chat_template_kwargs  */ params_base.default_template_kwargs,
1524                /* tmpls                 */ std::move(chat_templates),
1525                /* allow_image           */ mctx ? mtmd_support_vision(mctx) : false,
1526                /* allow_audio           */ mctx ? mtmd_support_audio (mctx) : false,
1527                /* allow_video           */ mctx ? mtmd_helper_support_video(mctx) : false,
1528                /* enable_thinking       */ enable_thinking,
1529                /* reasoning_budget      */ params_base.sampling.reasoning_budget_tokens,
1530                /* reasoning_budget_msg  */ params_base.sampling.reasoning_budget_message,
1531                /* media_path            */ params_base.media_path,
1532                /* force_pure_content    */ params_base.force_pure_content_parser
1533            };
1534
1535            {
1536                auto caps = common_chat_templates_get_caps(chat_params.tmpls.get());
1537                auto it = params_base.default_template_kwargs.find("preserve_reasoning");
1538                bool supported = caps.at("supports_preserve_reasoning");
1539                bool specified = params_base.preserve_reasoning_specified;
1540                // note: the kwarg is enabled by default if not specified explicitly, so check the value
1541                bool enabled = it != params_base.default_template_kwargs.end() && it->second == "true";
1542                if (supported) {
1543                    SRV_TRC("preserve_reasoning kwarg: %s\n",
1544                            it == params_base.default_template_kwargs.end() ? "unset (template default)" : it->second.c_str());
1545                } else {
1546                    SRV_TRC("%s", "preserve_reasoning kwarg: not supported by template\n");
1547                }
1548                if (supported && !specified) {
1549                    SRV_WRN("%s", "chat template supports preserving reasoning, it is enabled by default (may use more tokens, disable via --no-reasoning-preserve)\n");
1550                }
1551                if (supported && !enabled) {
1552                    SRV_INF("%s", "chat template supports preserving reasoning, consider enabling it via --reasoning-preserve\n");
1553                }
1554                if (!supported && specified && enabled) {
1555                    SRV_WRN("%s", "chat template does NOT support preserving reasoning, --reasoning-preserve has no effect\n");
1556                }
1557            }
1558        }
1559
1560        return true;
1561    }
1562
1563    server_slot * get_slot_by_id(int id_slot) {
1564        // note: allow id_slot to be out of bounds (wrap around)
1565        id_slot = id_slot % slots.size();
1566
1567        for (server_slot & slot : slots) {
1568            if (slot.id == id_slot) {
1569                return &slot;
1570            }
1571        }
1572
1573        return nullptr;
1574    }
1575
1576    server_slot * get_slot_by_cmpl_id(const std::string & cmpl_id) {
1577        if (cmpl_id.empty()) {
1578            return nullptr;
1579        }
1580
1581        for (server_slot & slot : slots) {
1582            if (slot.is_processing() && slot.task && slot.task->params.oaicompat_cmpl_id == cmpl_id) {
1583                return &slot;
1584            }
1585        }
1586
1587        return nullptr;
1588    }
1589
1590    server_slot * get_available_slot(const server_task & task) {
1591        server_slot * ret = nullptr;
1592
1593        bool update_cache = false;
1594
1595        // if a specific slot is requested, use it (still goes through cache update logic below)
1596        if (task.id_slot != -1) {
1597            ret = get_slot_by_id(task.id_slot);
1598            if (ret) {
1599                SLT_INF(*ret, "selected slot by id (%d)\n", task.id_slot);
1600            }
1601        }
1602
1603        // find the slot that has at least n% prompt similarity
1604        if (slot_prompt_similarity != 0.0f) {
1605            float f_sim_best = 0;
1606
1607            for (server_slot & slot : slots) {
1608                if (task.id_slot != -1 && slot.id != task.id_slot) {
1609                    continue;
1610                }
1611
1612                // skip the slot if it is not available
1613                if (slot.is_processing()) {
1614                    SLT_TRC(slot, " - skipping, is_processing = %d\n", slot.is_processing());
1615                    continue;
1616                }
1617
1618                const auto & tokens = slot.prompt.tokens;
1619
1620                // skip the slot if it does not contains cached tokens
1621                if (tokens.empty()) {
1622                    SLT_TRC(slot, "%s", " - skipping, slot is empty\n");
1623                    continue;
1624                }
1625
1626                // fraction of the Longest Common Prefix length with respect to the input prompt length
1627                const size_t lcp_len = tokens.get_common_prefix(task.tokens);
1628                const float f_sim_cur = float(lcp_len) / task.tokens.size();
1629
1630                SLT_TRC(slot, " - checking sim = %.3f (%zu/%zu) > %.3f\n", f_sim_cur, lcp_len, task.tokens.size(), slot_prompt_similarity);
1631
1632                // select the current slot if the criteria match
1633                if (f_sim_cur > f_sim_best && f_sim_cur > slot_prompt_similarity) {
1634                    f_sim_best = f_sim_cur;
1635
1636                    ret = &slot;
1637                }
1638            }
1639
1640            if (ret != nullptr) {
1641                const float f_keep = (f_sim_best*task.tokens.size()) / ret->prompt.tokens.size();
1642
1643                if (task.id_slot == -1) {
1644                    SLT_INF(*ret, "selected slot by LCP similarity, f_sim_best = %.3f (> %.3f thold), f_keep = %.3f\n",
1645                            f_sim_best, slot_prompt_similarity, f_keep);
1646                }
1647
1648                // if we are about to lose a large portion of the existing context - save it in the prompt cache
1649                if (f_keep < 0.5f) {
1650                    update_cache = true;
1651                }
1652            }
1653        }
1654
1655        // find the slot that has been least recently used
1656        if (ret == nullptr) {
1657            int64_t t_last = -1;
1658
1659            for (server_slot & slot : slots) {
1660                // skip the slot if it is not available
1661                if (slot.is_processing()) {
1662                    continue;
1663                }
1664
1665                // select the current slot if the criteria match
1666                if (!ret || slot.t_last_used <= t_last) {
1667                    t_last = slot.t_last_used;
1668                    ret = &slot;
1669                }
1670            }
1671
1672            if (ret != nullptr) {
1673                SLT_INF(*ret, "selected slot by LRU, t_last = %" PRId64 "\n", t_last);
1674
1675                update_cache = true;
1676            }
1677        }
1678
1679        if (ret) {
1680            update_cache = update_cache && prompt_cache;
1681
1682            // cache prompts only for completion tasks
1683            update_cache = update_cache && task.type == SERVER_TASK_TYPE_COMPLETION;
1684
1685            if (update_cache) {
1686                SRV_TRC("%s", "updating prompt cache\n");
1687
1688                const int64_t t_start = ggml_time_us();
1689
1690                ret->prompt_save(*prompt_cache);
1691
1692                if (!ret->prompt_load(*prompt_cache, task.tokens)) {
1693                    ret->prompt_clear();
1694                }
1695
1696                prompt_cache->update();
1697
1698                SRV_TRC("prompt cache update took %.2f ms\n", (ggml_time_us() - t_start) / 1000.0);
1699            }
1700        }
1701
1702        return ret;
1703    }
1704
1705    // return true if at least one slot has been cleared
1706    // TODO: improve logic
1707    //       - smarter decision which slot to clear (LRU or longest prompt?)
1708    //       - move slot to level 2 cache instead of removing?
1709    //       - instead of purging, try to store and resume later?
1710    bool try_clear_idle_slots() {
1711        bool res = false;
1712
1713        if (!params_base.kv_unified) {
1714            return res;
1715        }
1716
1717        for (auto & slot : slots) {
1718            if (slot.is_processing()) {
1719                continue;
1720            }
1721
1722            if (slot.prompt.n_tokens() > 0) {
1723                SRV_WRN("purging slot %d with %zu tokens\n", slot.id, slot.prompt.tokens.size());
1724
1725                slot.prompt_clear();
1726
1727                res = true;
1728
1729                // clear slots one by one
1730                break;
1731            }
1732        }
1733
1734        return res;
1735    }
1736
1737    std::vector<common_adapter_lora_info> construct_lora_list(const std::map<int, float> & config) const {
1738        std::vector<common_adapter_lora_info> output = params_base.lora_adapters; // copy
1739        for (size_t i = 0; i < output.size(); ++i) {
1740            auto it = config.find(i);
1741            if (it != config.end()) {
1742                output[i].scale = it->second;
1743            } else {
1744                output[i].scale = 0.0f;
1745            }
1746        }
1747        return output;
1748    }
1749
1750    bool launch_slot_with_task(server_slot & slot, server_task && task) {
1751        if (encoder_decoder) {
1752            if (!task.need_sampling() || task.is_parent() || task.is_child() || !task.params.lora.empty()) {
1753                send_error(task, "Encoder-decoder serving supports text generation with one completion and no LoRA", ERROR_TYPE_NOT_SUPPORTED);
1754                return false;
1755            }
1756            task.params.cache_prompt = false;
1757            task.params.n_cache_reuse = 0;
1758            task.params.sampling.backend_sampling = false;
1759        }
1760
1761        // process per-request lora adapters
1762        if (!task.params.lora.empty()) {
1763            auto task_loras = construct_lora_list(task.params.lora);
1764            if (!are_lora_equal(task_loras, slot.lora)) {
1765                // if lora has changed, check to see if the cache should be cleared
1766                if (lora_should_clear_cache(slot.lora, task_loras)) {
1767                    SLT_TRC(slot, "clearing cache for lora change. %zu loras -> %zu loras\n", slot.lora.size(), task.params.lora.size());
1768                    slot.prompt.clear();
1769                } else {
1770                    SLT_TRC(slot, "keeping cache for alora. %zu target loras\n", task_loras.size());
1771                }
1772                slot.lora = task_loras;
1773            }
1774        } else {
1775            slot.lora = params_base.lora_adapters;
1776        }
1777
1778        // if using alora, make sure it's only a single one requested and active
1779        size_t alora_invocation_start = task.tokens.size();
1780        if (lora_all_alora(slot.lora)) {
1781            const auto & enabled_ids = lora_get_enabled_ids(slot.lora);
1782            // TODO: This will error out if a user requests two aloras, but only
1783            // provides the activation string for one. We could, instead search
1784            // for all requested alora activation strings and then either keep
1785            // only the last one, or reject if multiple are found.
1786            if (enabled_ids.size() != 1) {
1787                send_error(task, "Cannot run multiple aLoRAs in a single request", ERROR_TYPE_INVALID_REQUEST);
1788                return false;
1789            }
1790            const auto & lora = slot.lora[enabled_ids[0]].ptr;
1791
1792            // get the pointer and count for the invocation tokens
1793            const uint64_t      n_invocation_tokens = llama_adapter_get_alora_n_invocation_tokens(lora);
1794            const llama_token * invocation_tokens   = llama_adapter_get_alora_invocation_tokens  (lora);
1795
1796            // scan backwards through the prompt tokens to find the last
1797            // occurrence of the invocation sequence
1798            int match_idx = static_cast<int>(n_invocation_tokens) - 1;
1799            for (int i = task.tokens.size() - 1; i >= 0; --i) {
1800                // the token in this position matches the next token to find in
1801                // the invocation sequence
1802                if (task.tokens[i] == invocation_tokens[match_idx]) {
1803                    // if it's a full match, we've found the start
1804                    if (match_idx == 0) {
1805                        alora_invocation_start = i;
1806                        break;
1807                    }
1808                    // otherwise, check the next token in the sequence
1809                    --match_idx;
1810                } else {
1811                    // no match in this position, so start looking over again
1812                    match_idx = static_cast<int>(n_invocation_tokens) - 1;
1813                }
1814            }
1815
1816            // if the activation string is not found, disable the alora
1817            if (alora_invocation_start == task.tokens.size()) {
1818                SLT_DBG(slot, "alora %zu requested, but not found. deactivating\n", enabled_ids[0]);
1819                slot.lora[enabled_ids[0]].scale = 0.0f;
1820            } else {
1821                SLT_DBG(slot, "alora %zu activated starting at %zu\n", enabled_ids[0], alora_invocation_start);
1822                slot.alora_invocation_start = alora_invocation_start;
1823            }
1824        }
1825
1826        if (!task.tokens.validate(ctx_tgt)) {
1827            send_error(task, "Prompt contains invalid tokens", ERROR_TYPE_INVALID_REQUEST);
1828            return false;
1829        }
1830
1831        SLT_DBG(slot, "launching slot : %s\n", safe_json_to_str(slot.to_json()).c_str());
1832
1833        // initialize samplers
1834        if (task.need_sampling()) {
1835            try {
1836                slot.smpl.reset(common_sampler_init(model_tgt, task.params.sampling));
1837            } catch (std::exception & e) {
1838                std::string err_msg = std::string("Failed to initialize samplers: ") + e.what();
1839                send_error(task, err_msg, ERROR_TYPE_INVALID_REQUEST);
1840                return false;
1841            }
1842
1843            const bool need_pre_sample_logits = task.params.sampling.n_probs > 0 && !task.params.post_sampling_probs;
1844
1845            bool use_backend_sampling = task.params.sampling.backend_sampling;
1846
1847            // TODO: getting pre sampling logits is not yet supported with backend sampling
1848            use_backend_sampling &= !need_pre_sample_logits;
1849
1850            // TODO: tmp until backend sampling is fully implemented
1851            if (use_backend_sampling) {
1852                llama_set_sampler(ctx_tgt, slot.id, common_sampler_get(slot.smpl.get()));
1853            } else {
1854                llama_set_sampler(ctx_tgt, slot.id, nullptr);
1855            }
1856
1857            SLT_TRC(slot, "sampler chain: %s\n", common_sampler_print(slot.smpl.get()).c_str());
1858            SLT_TRC(slot, "sampler params: \n%s\n", task.params.sampling.print().c_str());
1859
1860            if (spec && !common_speculative_get_synth_probs(spec.get()).empty()) {
1861                const uint32_t seed = task.params.sampling.seed == LLAMA_DEFAULT_SEED
1862                    ? std::random_device{}()
1863                    : task.params.sampling.seed;
1864                slot.spec_synth_rng.seed(seed);
1865            }
1866        } else {
1867            slot.smpl.reset();
1868        }
1869
1870        // the per-request limit takes priority over the global one
1871        slot.n_predict_max = task.params.n_predict != -1 ? task.params.n_predict : params_base.n_predict;
1872
1873        slot.task = std::make_unique<const server_task>(std::move(task));
1874
1875        slot.state = slot.task->is_child()
1876            ? SLOT_STATE_WAIT_OTHER // wait for the parent to process prompt
1877            : SLOT_STATE_STARTED;
1878
1879        // reset server kill-switch counter
1880        n_empty_consecutive = 0;
1881
1882        SLT_INF(slot, "processing task, is_child = %d\n", slot.task->is_child());
1883        return true;
1884    }
1885
1886    bool process_token(completion_token_output & result, server_slot & slot) {
1887        // remember which tokens were sampled - used for repetition penalties during sampling
1888        const std::string token_str = result.text_to_send;
1889        slot.sampled = result.tok;
1890
1891        slot.generated_text += token_str;
1892        if (slot.task->params.return_tokens) {
1893            slot.generated_tokens.push_back(result.tok);
1894        }
1895        slot.has_next_token = true;
1896
1897        // check if there is incomplete UTF-8 character at the end
1898        bool incomplete = validate_utf8(slot.generated_text) < slot.generated_text.size();
1899
1900        // search stop word and delete it
1901        if (!incomplete) {
1902            size_t pos = std::min(slot.n_sent_text, slot.generated_text.size());
1903
1904            const std::string str_test = slot.generated_text.substr(pos);
1905            bool send_text = true;
1906
1907            size_t stop_pos = slot.find_stopping_strings(str_test, token_str.size(), true);
1908            if (stop_pos != std::string::npos) {
1909                slot.generated_text.erase(
1910                    slot.generated_text.begin() + pos + stop_pos,
1911                    slot.generated_text.end());
1912                pos = std::min(slot.n_sent_text, slot.generated_text.size());
1913            } else if (slot.has_next_token && !llama_vocab_is_eog(vocab, result.tok) ) {
1914                stop_pos = slot.find_stopping_strings(str_test, token_str.size(), false);
1915                send_text = stop_pos == std::string::npos;
1916            }
1917
1918            // check if there is any token to predict
1919            if (send_text) {
1920                // no send the stop word in the response
1921                result.text_to_send = slot.generated_text.substr(pos, std::string::npos);
1922                slot.n_sent_text += result.text_to_send.size();
1923                // add the token to slot queue and cache
1924            } else {
1925                result.text_to_send = "";
1926            }
1927
1928            slot.add_token(result);
1929            if (slot.task->params.stream) {
1930                send_partial_response(slot, result, false);
1931            }
1932        }
1933
1934        if (incomplete) {
1935            slot.has_next_token = true;
1936        }
1937
1938        // if context shifting is disabled, make sure that we don't run out of context
1939        if (!params_base.ctx_shift && slot.prompt.n_tokens() + 1 >= slot.n_ctx) {
1940            slot.truncated      = true;
1941            slot.stop           = STOP_TYPE_LIMIT;
1942            slot.has_next_token = false;
1943
1944            SLT_DBG(slot, "stopped due to running out of context capacity, prompt.n_tokens() = %d, task.n_tokens = %d, n_gen = %d, n_ctx = %d\n",
1945                    slot.prompt.n_tokens(), slot.task->n_tokens(), (int) slot.stats.n_gen, slot.n_ctx);
1946        }
1947
1948        // check the limits
1949        if (slot.stats.n_gen > 0 && slot.has_next_token && !slot.has_budget()) {
1950            slot.stop           = STOP_TYPE_LIMIT;
1951            slot.has_next_token = false;
1952
1953            SLT_DBG(slot, "stopped by limit, n_gen = %d, n_predict = %d\n", (int) slot.stats.n_gen, slot.task->params.n_predict);
1954        }
1955
1956        if (slot.has_new_line) {
1957            // require that each new line has a whitespace prefix (i.e. indentation) of at least slot.params.n_indent
1958            if (slot.task->params.n_indent > 0) {
1959                // check the current indentation
1960                // TODO: improve by not doing it more than once for each new line
1961                if (slot.last_nl_pos > 0) {
1962                    size_t pos = slot.last_nl_pos;
1963
1964                    int n_indent = 0;
1965                    while (pos < slot.generated_text.size() && (slot.generated_text[pos] == ' ' || slot.generated_text[pos] == '\t')) {
1966                        n_indent++;
1967                        pos++;
1968                    }
1969
1970                    if (pos < slot.generated_text.size() && n_indent < slot.task->params.n_indent) {
1971                        slot.stop           = STOP_TYPE_LIMIT;
1972                        slot.has_next_token = false;
1973
1974                        // cut the last line
1975                        slot.generated_text.erase(pos, std::string::npos);
1976
1977                        SLT_DBG(slot, "stopped by indentation limit, n_gen = %d, n_indent = %d\n", (int) slot.stats.n_gen, n_indent);
1978                    }
1979                }
1980
1981                // find the next new line
1982                {
1983                    const size_t pos = slot.generated_text.find('\n', slot.last_nl_pos);
1984
1985                    if (pos != std::string::npos) {
1986                        slot.last_nl_pos = pos + 1;
1987                    }
1988                }
1989            }
1990        }
1991
1992        // check if there is a new line in the generated text
1993        if (result.text_to_send.find('\n') != std::string::npos) {
1994            slot.has_new_line = true;
1995
1996            // if we have seen a new line, we stop after a certain time limit, but only upon another new line
1997            if (slot.task->params.t_max_predict_ms > 0 && slot.stats.t_gen_ms() > slot.task->params.t_max_predict_ms) {
1998                slot.stop           = STOP_TYPE_LIMIT;
1999                slot.has_next_token = false;
2000
2001                SLT_DBG(slot, "stopped by time limit, n_gen = %d, t_max_predict_ms = %d ms\n", (int) slot.stats.n_gen, (int) slot.task->params.t_max_predict_ms);
2002            }
2003        }
2004
2005        if (llama_vocab_is_eog(vocab, result.tok)) {
2006            slot.stop           = STOP_TYPE_EOS;
2007            slot.has_next_token = false;
2008
2009            SLT_DBG(slot, "%s", "stopped by EOS\n");
2010        }
2011
2012        SLT_DBG(slot, "n_gen = %d, n_remaining = %d, next token: %5d '%s'\n", (int) slot.stats.n_gen, slot.n_remaining(), result.tok, token_str.c_str());
2013
2014        return slot.has_next_token; // continue
2015    }
2016
2017    void populate_token_probs(const server_slot & slot, completion_token_output & result, bool post_sampling, bool special, int idx) const {
2018        const size_t n_probs_request = slot.task->params.sampling.n_probs;
2019
2020        if (post_sampling) {
2021            const auto * cur_p = common_sampler_get_candidates(slot.smpl.get(), true);
2022            const size_t max_probs = cur_p->size;
2023            const size_t n_probs = std::min(max_probs, n_probs_request);
2024
2025            // set probability for sampled token
2026            for (size_t i = 0; i < max_probs; i++) {
2027                if (cur_p->data[i].id == result.tok) {
2028                    result.prob = cur_p->data[i].p;
2029                    break;
2030                }
2031            }
2032
2033            // set probability for top n_probs tokens
2034            result.probs.reserve(n_probs);
2035            for (size_t i = 0; i < n_probs; i++) {
2036                // Some samplers do return 0.0 probabilities, others don't.
2037                // Filter 0.0 probailities, to ensure the behavior is consistent.
2038                if (cur_p->data[i].p == 0.0) {
2039                    break;
2040                }
2041
2042                result.probs.push_back({
2043                    cur_p->data[i].id,
2044                    common_token_to_piece(ctx_tgt, cur_p->data[i].id, special),
2045                    cur_p->data[i].p
2046                });
2047            }
2048        } else {
2049            std::vector<llama_token_data> cur = get_token_probabilities(ctx_tgt, idx, n_probs_request);
2050            const size_t max_probs = cur.size();
2051            const size_t n_probs = std::min(max_probs, n_probs_request);
2052
2053            // set probability for sampled token
2054            for (size_t i = 0; i < max_probs; i++) {
2055                // set probability for sampled token
2056                if (cur[i].id == result.tok) {
2057                    result.prob = cur[i].p;
2058                    break;
2059                }
2060            }
2061
2062            // set probability for top n_probs tokens
2063            result.probs.reserve(n_probs);
2064            for (size_t i = 0; i < n_probs; i++) {
2065                result.probs.push_back({
2066                    cur[i].id,
2067                    common_token_to_piece(ctx_tgt, cur[i].id, special),
2068                    cur[i].p
2069                });
2070            }
2071        }
2072    }
2073
2074    void send_error(const server_task & task, const std::string & error, const enum error_type type = ERROR_TYPE_SERVER) {
2075        send_error(task.id, error, type);
2076    }
2077
2078    void send_error(const server_slot & slot, const std::string & error, const enum error_type type = ERROR_TYPE_SERVER) {
2079        send_error(slot.task->id, error, type, slot.task->n_tokens(), slot.n_ctx);
2080    }
2081
2082    void send_error(const int id_task, const std::string & error, const enum error_type type = ERROR_TYPE_SERVER, const int32_t n_prompt_tokens = 0, const int32_t n_ctx = 0) {
2083        SRV_ERR("task id = %d, error: %s\n", id_task, error.c_str());
2084
2085        if (type == ERROR_TYPE_EXCEED_CONTEXT_SIZE) {
2086            GGML_ASSERT(n_ctx > 0 && n_prompt_tokens > 0);
2087        }
2088
2089        auto res = std::make_unique<server_task_result_error>();
2090        res->id              = id_task;
2091        res->err_type        = type;
2092        res->err_msg         = error;
2093        res->n_prompt_tokens = n_prompt_tokens;
2094        res->n_ctx           = n_ctx;
2095
2096        queue_results.send(std::move(res));
2097    }
2098
2099    void send_partial_response(server_slot & slot, const completion_token_output & tkn, bool is_progress, bool is_begin = false) {
2100        auto res = std::make_unique<server_task_result_cmpl_partial>();
2101
2102        res->id    = slot.task->id;
2103        res->index = slot.task->index;
2104
2105        if (is_progress) {
2106            res->is_progress        = true;
2107            res->progress.total     = slot.task->n_tokens();
2108            res->progress.cache     = slot.stats.n_prompt_cached;
2109            res->progress.processed = slot.prompt.tokens.size();
2110            res->progress.time_ms   = slot.stats.t_elapsed_us() / 1000;
2111        }
2112        if (is_begin) {
2113            res->is_begin = true;
2114        } else {
2115            res->content = tkn.text_to_send;
2116            res->tokens  = { tkn.tok };
2117        }
2118
2119        res->n_decoded             = slot.stats.n_gen;
2120        res->n_prompt_tokens       = slot.task->n_tokens();
2121        res->n_prompt_tokens_cache = slot.stats.n_prompt_cached;
2122        res->post_sampling_probs   = slot.task->params.post_sampling_probs;
2123
2124        res->verbose           = slot.task->params.verbose;
2125        res->res_type          = slot.task->params.res_type;
2126        res->oaicompat_model   = slot.task->params.oaicompat_model;
2127        res->oaicompat_cmpl_id = slot.task->params.oaicompat_cmpl_id;
2128
2129        // populate res.probs_output
2130        if (slot.task->params.sampling.n_probs > 0) {
2131            res->prob_output = tkn; // copy the token probs
2132        }
2133
2134        // populate timings if this is final response or timings_per_token is enabled
2135        if (slot.stop != STOP_TYPE_NONE || slot.task->params.timings_per_token) {
2136            res->stats = slot.stats;
2137        }
2138
2139        queue_results.send(std::move(res));
2140    }
2141
2142    void send_final_response(server_slot & slot) {
2143        auto res = std::make_unique<server_task_result_cmpl_final>();
2144
2145        res->id      = slot.task->id;
2146        res->id_slot = slot.id;
2147
2148        res->index = slot.task->index;
2149
2150        // keep copy of last generated text for debugging purposes
2151        if (slots_debug) {
2152            slot.debug_generated_text = slot.generated_text;
2153        }
2154
2155        // in stream mode, content and tokens are already in last partial chunk
2156        if (slot.task->params.stream) {
2157            res->content     = "";
2158            res->tokens      = llama_tokens{};
2159        } else {
2160            res->content     = std::move(slot.generated_text);
2161            res->tokens      = std::move(slot.generated_tokens);
2162        }
2163        res->stats           = slot.stats;
2164        res->prompt          = slot.task->tokens.detokenize(ctx_tgt, true);
2165        res->response_fields = std::move(slot.task->params.response_fields);
2166
2167        res->truncated             = slot.truncated;
2168        res->n_decoded             = slot.stats.n_gen;
2169        res->n_prompt_tokens       = slot.task->n_tokens();
2170        res->n_prompt_tokens_cache = slot.stats.n_prompt_cached;
2171        res->n_tokens_cached       = slot.prompt.n_tokens();
2172        res->has_new_line          = slot.has_new_line;
2173        res->stopping_word         = slot.stopping_word;
2174        res->stop                  = slot.stop;
2175        res->post_sampling_probs   = slot.task->params.post_sampling_probs;
2176
2177        res->verbose           = slot.task->params.verbose;
2178        res->stream            = slot.task->params.stream;
2179        res->include_usage     = slot.task->params.include_usage;
2180        res->res_type          = slot.task->params.res_type;
2181        res->oaicompat_model   = slot.task->params.oaicompat_model;
2182        res->oaicompat_cmpl_id = slot.task->params.oaicompat_cmpl_id;
2183
2184        // populate res.probs_output
2185        if (slot.task->params.sampling.n_probs > 0) {
2186            if (!slot.task->params.stream && slot.stop == STOP_TYPE_WORD) {
2187                const llama_tokens stop_word_toks = common_tokenize(ctx_tgt, slot.stopping_word, false);
2188
2189                size_t safe_offset = std::min(slot.generated_token_probs.size(), stop_word_toks.size());
2190                res->probs_output = std::vector<completion_token_output>(
2191                        slot.generated_token_probs.begin(),
2192                        slot.generated_token_probs.end() - safe_offset);
2193            } else {
2194                res->probs_output = std::vector<completion_token_output>(
2195                        slot.generated_token_probs.begin(),
2196                        slot.generated_token_probs.end());
2197            }
2198        }
2199
2200        res->generation_params = slot.task->params; // copy the parameters
2201
2202        queue_results.send(std::move(res));
2203    }
2204
2205    void send_embedding(const server_slot & slot, const llama_batch & batch) {
2206        auto res = std::make_unique<server_task_result_embd>();
2207        res->id        = slot.task->id;
2208        res->index     = slot.task->index;
2209        res->n_tokens  = slot.task->n_tokens();
2210        res->res_type  = slot.task->params.res_type;
2211
2212        const int n_embd_out = llama_model_n_embd_out(model_tgt);
2213
2214        std::vector<float> embd_res(n_embd_out, 0.0f);
2215
2216        for (int i = 0; i < batch.n_tokens; ++i) {
2217            if (!batch.logits[i] || batch.seq_id[i][0] != slot.id) {
2218                continue;
2219            }
2220
2221            const float * embd = nullptr;
2222            if (llama_pooling_type(slot.ctx_tgt) == LLAMA_POOLING_TYPE_NONE) {
2223                embd = llama_get_embeddings_ith(slot.ctx_tgt, i);
2224            } else {
2225                embd = llama_get_embeddings_seq(slot.ctx_tgt, batch.seq_id[i][0]);
2226            }
2227
2228            if (embd == nullptr) {
2229                SLT_ERR(slot, "failed to get embeddings, token = %d, seq_id = %d\n", batch.token[i], batch.seq_id[i][0]);
2230
2231                res->embedding.push_back(std::vector<float>(n_embd_out, 0.0f));
2232                continue;
2233            }
2234
2235            // normalize only when there is pooling
2236            if (llama_pooling_type(slot.ctx_tgt) != LLAMA_POOLING_TYPE_NONE) {
2237                common_embd_normalize(embd, embd_res.data(), n_embd_out, slot.task->params.embd_normalize);
2238                res->embedding.push_back(embd_res);
2239                break;
2240            }
2241
2242            res->embedding.emplace_back(embd, embd + n_embd_out);
2243        }
2244
2245        SLT_DBG(slot, "%s", "sending embeddings\n");
2246
2247        queue_results.send(std::move(res));
2248    }
2249
2250    void send_rerank(const server_slot & slot, const llama_batch & batch) {
2251        auto res = std::make_unique<server_task_result_rerank>();
2252        res->id       = slot.task->id;
2253        res->index    = slot.task->index;
2254        res->n_tokens = slot.task->n_tokens();
2255
2256        for (int i = 0; i < batch.n_tokens; ++i) {
2257            if (!batch.logits[i] || batch.seq_id[i][0] != slot.id) {
2258                continue;
2259            }
2260
2261            const float * embd = llama_get_embeddings_seq(ctx_tgt, batch.seq_id[i][0]);
2262            if (embd == NULL) {
2263                embd = llama_get_embeddings_ith(ctx_tgt, i);
2264            }
2265
2266            if (embd == NULL) {
2267                SLT_ERR(slot, "failed to get embeddings, token = %d, seq_id = %d\n", batch.token[i], batch.seq_id[i][0]);
2268
2269                res->score = -1e6;
2270                continue;
2271            }
2272
2273            res->score = embd[0];
2274        }
2275
2276        SLT_DBG(slot, "sending rerank result, res.score = %f\n", res->score);
2277
2278        queue_results.send(std::move(res));
2279    }
2280
2281    //
2282    // Functions to process the task
2283    //
2284
2285    // tokenize the input if it's set by CLI, return false on error
2286    bool tokenize_cli_input(server_task & task) {
2287        try {
2288            auto & prompt = task.cli_prompt;
2289            if (mctx != nullptr) {
2290                task.tokens = process_mtmd_prompt(mctx, prompt, task.cli_files, init_opt);
2291            } else {
2292                task.tokens = std::move(tokenize_input_prompts(vocab, mctx, prompt, true, true, init_opt)[0]);
2293            }
2294            task.cli_prompt.clear();
2295            task.cli_files.clear();
2296        } catch (const std::exception & e) {
2297            send_error(task, std::string("Failed to format input: ") + e.what(), ERROR_TYPE_INVALID_REQUEST);
2298            return false;
2299        }
2300        return true;
2301    }
2302
2303    std::vector<server_slot *> get_free_slots(size_t n_slots_needed, int exclude_id_slot) {
2304        std::vector<server_slot *> free_slots;
2305        for (auto & slot : slots) {
2306            if (!slot.is_processing() && slot.id != exclude_id_slot) {
2307                free_slots.push_back(&slot);
2308            }
2309            if (free_slots.size() >= n_slots_needed) {
2310                break;
2311            }
2312        }
2313        return free_slots;
2314    }
2315
2316    // launch multiple slots for parent + child tasks
2317    bool launch_slots_with_parent_task(server_slot & parent_slot, std::vector<server_slot *> & child_slots, server_task && parent_task) {
2318        GGML_ASSERT(!parent_slot.is_processing());
2319        GGML_ASSERT(parent_task.is_parent());
2320        GGML_ASSERT(child_slots.size() == parent_task.child_tasks.size());
2321
2322        int id_parent = parent_task.id;
2323
2324        SRV_TRC("launching slots for parent task id_task = %d with %zu child tasks\n", id_parent, parent_task.child_tasks.size());
2325
2326        // to be called in case of failure to release all launched slots
2327        auto release_slots = [this, id_parent]() {
2328            for (auto & slot : slots) {
2329                if (slot.is_processing() && (
2330                        slot.task->id == id_parent ||
2331                        slot.task->id_parent == id_parent
2332                )) {
2333                    slot.release();
2334                }
2335            }
2336        };
2337
2338        // launch all child tasks first
2339        size_t idx = 0;
2340        GGML_ASSERT(child_slots.size() == parent_task.child_tasks.size());
2341        for (auto * slot : child_slots) {
2342            int id_child = parent_task.child_tasks[idx].id;
2343            if (!launch_slot_with_task(*slot, std::move(parent_task.child_tasks[idx]))) {
2344                SRV_ERR("failed to launch slot with child task, id_task = %d\n", id_child);
2345                release_slots();
2346                return false;
2347            }
2348            idx++;
2349        }
2350
2351        // finally, launch the parent task
2352        if (!launch_slot_with_task(parent_slot, std::move(parent_task))) {
2353            SRV_ERR("failed to launch slot with task, id_task = %d\n", id_parent);
2354            release_slots();
2355            return false;
2356        }
2357
2358        return true;
2359    }
2360
2361    // n_tokens_cur: the number of tokens added to the batch for the current slot
2362    void create_checkpoint(server_slot & slot, const int64_t n_tokens_cur, llama_pos pos_min, llama_pos pos_max) {
2363        const int id_task = slot.task->id;
2364
2365        // evict checkpoints within min-step of a previous checkpoint, unless they were
2366        // created by the current task
2367        // only when the list is full, otherwise short prompts keep just the oldest checkpoint
2368        int64_t last = -1;
2369        for (auto it = slot.prompt.checkpoints.begin();
2370                slot.prompt.checkpoints.size() + 1 >= (size_t) params_base.n_ctx_checkpoints &&
2371                it != slot.prompt.checkpoints.end(); ) {
2372            if (it->id_task != id_task && last >= 0 && it->n_tokens <= last + params_base.checkpoint_min_step) {
2373                SLT_TRC(slot, "erasing context checkpoint too close to an earlier one (pos_min = %d, pos_max = %d, n_tokens = %" PRId64 ", size = %.3f MiB)\n",
2374                        it->pos_min, it->pos_max, it->n_tokens, (float) it->size() / 1024 / 1024);
2375
2376                it = slot.prompt.checkpoints.erase(it);
2377                continue;
2378            }
2379
2380            last = it->n_tokens;
2381            ++it;
2382        }
2383
2384        while (slot.prompt.checkpoints.size() >= (size_t) params_base.n_ctx_checkpoints) {
2385            // make room for the new checkpoint, if needed
2386            const auto & cur = slot.prompt.checkpoints.front();
2387
2388            SLT_WRN(slot, "erasing old context checkpoint (pos_min = %d, pos_max = %d, n_tokens = %" PRId64 ", size = %.3f MiB)\n",
2389                    cur.pos_min, cur.pos_max, cur.n_tokens, (float) cur.size() / 1024 / 1024);
2390
2391            slot.prompt.checkpoints.erase(slot.prompt.checkpoints.begin());
2392        }
2393
2394        // replace an existing checkpoint at the same n_tokens instead of appending a duplicate
2395        {
2396            const int64_t n_tokens_new = slot.prompt.n_tokens() - n_tokens_cur;
2397            for (auto it = slot.prompt.checkpoints.begin(); it != slot.prompt.checkpoints.end(); ) {
2398                if (it->n_tokens == n_tokens_new) {
2399                    SLT_TRC(slot, "superseding context checkpoint at n_tokens = %" PRId64 "\n", it->n_tokens);
2400                    it = slot.prompt.checkpoints.erase(it);
2401                } else {
2402                    ++it;
2403                }
2404            }
2405        }
2406
2407        auto & cur = slot.prompt.checkpoints.emplace_back();
2408
2409        cur.id_task = id_task;
2410
2411        // [TAG_CHECKPOINTS_FIX_POS_MIN]
2412        // TODO: here we incorrectly deterimne that the saved checkpoint data covers the [pos_min, pos_max] range
2413        //       this is not true for SWA models: https://github.com/ggml-org/llama.cpp/pull/24411#issuecomment-4677983225
2414        cur.update_pos(slot.prompt.n_tokens() - n_tokens_cur, pos_min, pos_max);
2415
2416        cur.update_tgt(ctx_tgt, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
2417        cur.update_dft(ctx_dft, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
2418        // stash the draft's speculative state with the checkpoint
2419        common_speculative_get_state(spec.get(), slot.id, cur.data_spec);
2420
2421        SLT_TRC(slot,
2422                "created context checkpoint %d of %d (pos_min = %d, pos_max = %d, n_tokens = %" PRId64 ", size = %.3f MiB)\n",
2423                (int) slot.prompt.checkpoints.size(), params_base.n_ctx_checkpoints, cur.pos_min,
2424                cur.pos_max, cur.n_tokens, (float) cur.size() / 1024 / 1024);
2425    }
2426
2427    // returns false to decline the task, it is offered again after the decode is done
2428    bool process_single_task(server_task && task, bool is_yielding) {
2429        // while yielding, an encode / decode is running and only reading the server state is safe
2430        if (is_yielding && task.type != SERVER_TASK_TYPE_METRICS && task.type != SERVER_TASK_TYPE_SLOT_GET) {
2431            SRV_DBG("decoding, decline task, id_task = %d\n", task.id);
2432            return false;
2433        }
2434
2435        switch (task.type) {
2436            case SERVER_TASK_TYPE_COMPLETION:
2437            case SERVER_TASK_TYPE_INFILL:
2438            case SERVER_TASK_TYPE_EMBEDDING:
2439            case SERVER_TASK_TYPE_RERANK:
2440                {
2441                    // special case: if input is provided via CLI, tokenize it first
2442                    // otherwise, no need to tokenize as it's already done inside the HTTP thread
2443                    if (task.cli) {
2444                        if (!tokenize_cli_input(task)) {
2445                            break;
2446                        }
2447                    }
2448
2449                    const int id_task = task.id;
2450
2451                    server_slot * slot = get_available_slot(task);
2452
2453                    //
2454                    // slot scheduling logic
2455                    //
2456
2457                    if (slot == nullptr) {
2458                        // if no slot is available, we defer this task for processing later
2459                        SRV_DBG("no slot is available, defer task, id_task = %d\n", id_task);
2460                        queue_tasks.defer(std::move(task));
2461                        break;
2462                    }
2463
2464                    if (slot->is_processing()) {
2465                        // if requested slot is unavailable, we defer this task for processing later
2466                        SRV_DBG("requested slot is unavailable, defer task, id_task = %d\n", id_task);
2467                        queue_tasks.defer(std::move(task));
2468                        break;
2469                    }
2470
2471                    if (task.is_parent()) {
2472                        // try getting free slots for all child tasks
2473                        size_t n_child_tasks = task.child_tasks.size();
2474                        std::vector<server_slot *> child_slots = get_free_slots(n_child_tasks, slot->id);
2475                        if (child_slots.size() < n_child_tasks) {
2476                            SRV_DBG("not enough free slots for child tasks, n_free = %zu, n_children = %zu, defer task, id_task = %d\n", child_slots.size(), n_child_tasks, id_task);
2477                            queue_tasks.defer(std::move(task));
2478                            break;
2479                        }
2480                        if (!launch_slots_with_parent_task(*slot, child_slots, std::move(task))) {
2481                            SRV_ERR("failed to launch slot with parent task, id_task = %d\n", id_task);
2482                            break; // drop the task
2483                        }
2484                    } else if (!launch_slot_with_task(*slot, std::move(task))) {
2485                        SRV_ERR("failed to launch slot with task, id_task = %d\n", id_task);
2486                        break; // drop the task
2487                    }
2488
2489                    if (params_base.cache_idle_slots) {
2490                        for (auto & slot : slots) {
2491                            if (!slot.is_processing()) {
2492                                SLT_TRC(slot, "%s", "saving idle slot to prompt cache\n");
2493
2494                                if (slot.prompt_save(*prompt_cache)) {
2495                                    SLT_DBG(slot, "%s", "__TEST_TAG_CACHE_IDLE_SLOT__\n");
2496                                    prompt_cache->update();
2497                                }
2498
2499                                if (params_base.kv_unified) {
2500                                    // [TAG_IDLE_SLOT_CLEAR]
2501                                    slot.prompt_clear();
2502                                }
2503                            }
2504                        }
2505                    }
2506                } break;
2507            case SERVER_TASK_TYPE_CANCEL:
2508                {
2509                    // release slot linked with the task id
2510                    for (auto & slot : slots) {
2511                        if (slot.task && slot.task->id == task.id_target) {
2512                            slot.release();
2513                            break;
2514                        }
2515                    }
2516                } break;
2517            case SERVER_TASK_TYPE_CONTROL:
2518                {
2519                    auto res = std::make_unique<server_task_result_control>();
2520                    res->id = task.id;
2521
2522                    server_slot * slot = get_slot_by_cmpl_id(task.params.control_cmpl_id);
2523                    if (slot == nullptr) {
2524                        SRV_WRN("control %s on unknown completion id=%s, no live slot\n",
2525                                task.params.control_action.c_str(), task.params.control_cmpl_id.c_str());
2526                        res->success = false;
2527                        res->message = "no active completion for this id";
2528                        queue_results.send(std::move(res));
2529                        break;
2530                    }
2531
2532                    if (task.params.control_action == "reasoning_end") {
2533                        // the budget sampler only exists when reasoning control was armed
2534                        if (!slot->task->params.sampling.reasoning_control) {
2535                            res->success = false;
2536                            res->message = "reasoning control not enabled for this completion";
2537                            queue_results.send(std::move(res));
2538                            break;
2539                        }
2540                        // act on the live slot mid generation, never defer
2541                        common_sampler_reasoning_budget_force(slot->smpl.get());
2542                        res->success = true;
2543                    } else {
2544                        res->success = false;
2545                        res->message = "unknown control action";
2546                    }
2547
2548                    queue_results.send(std::move(res));
2549                } break;
2550            case SERVER_TASK_TYPE_NEXT_RESPONSE:
2551                {
2552                    // do nothing
2553                } break;
2554            case SERVER_TASK_TYPE_METRICS:
2555                {
2556                    int n_processing_slots = 0;
2557
2558                    for (server_slot & slot : slots) {
2559                        if (slot.is_processing()) {
2560                            n_processing_slots++;
2561                        }
2562                    }
2563                    SRV_DBG("n_processing_slots = %d\n", n_processing_slots);
2564
2565                    auto res = std::make_unique<server_task_result_metrics>();
2566                    res->id                  = task.id;
2567                    res->n_processing_slots  = n_processing_slots;
2568                    res->n_tasks_deferred    = queue_tasks.queue_tasks_deferred_size();
2569                    res->metrics             = metrics;
2570
2571                    if (task.metrics_reset_bucket) {
2572                        metrics.reset_bucket();
2573                    }
2574                    queue_results.send(std::move(res));
2575                } break;
2576            case SERVER_TASK_TYPE_SLOT_GET:
2577                {
2578                    json slots_data = json::array();
2579
2580                    int n_idle_slots = 0;
2581
2582                    for (server_slot & slot : slots) {
2583                        if (!slot.is_processing()) {
2584                            n_idle_slots++;
2585                        }
2586
2587                        slots_data.push_back(slot.to_json(slots_debug == 0));
2588                    }
2589                    SRV_DBG("n_idle_slots = %d\n", n_idle_slots);
2590
2591                    auto res = std::make_unique<server_task_result_slots>();
2592                    res->id           = task.id;
2593                    res->slots_data   = std::move(slots_data);
2594                    res->n_idle_slots = n_idle_slots;
2595
2596                    queue_results.send(std::move(res));
2597                } break;
2598            case SERVER_TASK_TYPE_SLOT_SAVE:
2599                {
2600                    const int id_slot = task.slot_action.id_slot;
2601                    server_slot * slot = get_slot_by_id(id_slot);
2602                    if (slot == nullptr) {
2603                        send_error(task, "Invalid slot ID", ERROR_TYPE_INVALID_REQUEST);
2604                        break;
2605                    }
2606                    if (slot->is_processing()) {
2607                        // if requested slot is unavailable, we defer this task for processing later
2608                        SRV_DBG("requested slot is unavailable, defer task, id_task = %d\n", task.id);
2609                        queue_tasks.defer(std::move(task));
2610                        break;
2611                    }
2612
2613                    const int64_t t_start = ggml_time_us();
2614
2615                    std::string filename = task.slot_action.filename;
2616                    std::string filepath = task.slot_action.filepath;
2617
2618                    std::vector<char> packed;
2619                    try {
2620                        packed = slot->prompt.tokens.serialize();
2621                    } catch (const std::exception & err) {
2622                        send_error(task, err.what(), ERROR_TYPE_NOT_SUPPORTED);
2623                        break;
2624                    }
2625
2626                    GGML_ASSERT(packed.size() % sizeof(llama_token) == 0);
2627                    const size_t nwrite = llama_state_seq_save_file(
2628                        ctx_tgt, filepath.c_str(), slot->id,
2629                        reinterpret_cast<const llama_token *>(packed.data()), packed.size() / sizeof(llama_token));
2630                    if (nwrite == 0) {
2631                        send_error(task, "Unable to save slot", ERROR_TYPE_SERVER);
2632                        break;
2633                    }
2634
2635                    const int64_t t_end = ggml_time_us();
2636                    const double t_save_ms = (t_end - t_start) / 1000.0;
2637
2638                    auto res = std::make_unique<server_task_result_slot_save_load>();
2639                    res->id       = task.id;
2640                    res->id_slot  = id_slot;
2641                    res->filename = filename;
2642                    res->is_save  = true;
2643                    res->n_tokens = slot->prompt.tokens.size();
2644                    res->n_bytes  = nwrite;
2645                    res->t_ms     = t_save_ms;
2646                    queue_results.send(std::move(res));
2647                } break;
2648            case SERVER_TASK_TYPE_SLOT_RESTORE:
2649                {
2650                    const int id_slot = task.slot_action.id_slot;
2651                    server_slot * slot = get_slot_by_id(id_slot);
2652                    if (slot == nullptr) {
2653                        send_error(task, "Invalid slot ID", ERROR_TYPE_INVALID_REQUEST);
2654                        break;
2655                    }
2656                    if (slot->is_processing()) {
2657                        // if requested slot is unavailable, we defer this task for processing later
2658                        SRV_DBG("requested slot is unavailable, defer task, id_task = %d\n", task.id);
2659                        queue_tasks.defer(std::move(task));
2660                        break;
2661                    }
2662
2663                    const int64_t t_start = ggml_time_us();
2664
2665                    std::string filename = task.slot_action.filename;
2666                    std::string filepath = task.slot_action.filepath;
2667
2668                    size_t nread = 0;
2669                    try {
2670                        size_t n_packed = 0;
2671                        llama_tokens packed;
2672                        nread = llama_state_seq_load_file(ctx_tgt, filepath.c_str(), slot->id, nullptr, 0, &n_packed);
2673                        if (nread != 0) {
2674                            packed.resize(std::max<size_t>(1, n_packed));
2675                            nread = llama_state_seq_load_file(ctx_tgt, filepath.c_str(), slot->id, packed.data(), packed.size(), &n_packed);
2676                        }
2677                        if (nread == 0) {
2678                            throw std::runtime_error("No available space in KV cache or invalid slot save file");
2679                        }
2680                        packed.resize(n_packed);
2681
2682                        server_tokens restored = server_tokens::deserialize(packed, mctx != nullptr);
2683
2684                        if (restored.size() > (size_t) slot->n_ctx) {
2685                            throw std::runtime_error("Restored prompt does not fit in the slot context");
2686                        }
2687
2688                        if (!restored.validate(ctx_tgt)) {
2689                            throw std::runtime_error("Invalid tokens in slot save file");
2690                        }
2691
2692                        slot->prompt.clear();
2693                        slot->prompt.tokens = std::move(restored);
2694                    } catch (const std::exception & err) {
2695                        slot->prompt_clear();
2696                        send_error(task, std::string("Unable to restore slot: ") + err.what(), ERROR_TYPE_INVALID_REQUEST);
2697                        break;
2698                    }
2699
2700                    const int64_t t_end = ggml_time_us();
2701                    const double t_restore_ms = (t_end - t_start) / 1000.0;
2702
2703                    auto res = std::make_unique<server_task_result_slot_save_load>();
2704                    res->id       = task.id;
2705                    res->id_slot  = id_slot;
2706                    res->filename = filename;
2707                    res->is_save  = false;
2708                    res->n_tokens = slot->prompt.tokens.size();
2709                    res->n_bytes  = nread;
2710                    res->t_ms     = t_restore_ms;
2711                    queue_results.send(std::move(res));
2712                } break;
2713            case SERVER_TASK_TYPE_SLOT_ERASE:
2714                {
2715                    const int id_slot = task.slot_action.id_slot;
2716                    server_slot * slot = get_slot_by_id(id_slot);
2717                    if (slot == nullptr) {
2718                        send_error(task, "Invalid slot ID", ERROR_TYPE_INVALID_REQUEST);
2719                        break;
2720                    }
2721                    if (slot->is_processing()) {
2722                        // if requested slot is unavailable, we defer this task for processing later
2723                        SRV_DBG("requested slot is unavailable, defer task, id_task = %d\n", task.id);
2724                        queue_tasks.defer(std::move(task));
2725                        break;
2726                    }
2727
2728                    // Erase token cache
2729                    const size_t n_erased = slot->prompt.tokens.size();
2730
2731                    slot->prompt_clear();
2732
2733                    auto res = std::make_unique<server_task_result_slot_erase>();
2734                    res->id       = task.id;
2735                    res->id_slot  = id_slot;
2736                    res->n_erased = n_erased;
2737                    queue_results.send(std::move(res));
2738                } break;
2739            case SERVER_TASK_TYPE_GET_LORA:
2740                {
2741                    // TODO @ngxson : make lora_adapters a dedicated member of server_context
2742                    auto & loras = params_base.lora_adapters;
2743                    auto res = std::make_unique<server_task_result_get_lora>();
2744                    res->id = task.id;
2745                    for (size_t i = 0; i < loras.size(); ++i) {
2746                        auto & lora = loras[i];
2747                        std::string alora_invocation_string = "";
2748                        const uint64_t n_alora_tokens = llama_adapter_get_alora_n_invocation_tokens(lora.ptr);
2749                        llama_tokens alora_invocation_tokens;
2750                        if (n_alora_tokens) {
2751                            const llama_token * alora_tokens = llama_adapter_get_alora_invocation_tokens(lora.ptr);
2752                            for (uint64_t j = 0; j < n_alora_tokens; ++j) {
2753                                alora_invocation_string += common_token_to_piece(vocab, alora_tokens[j]);
2754                                alora_invocation_tokens.push_back(alora_tokens[j]);
2755                            }
2756                        }
2757                        res->loras.push_back(server_task_result_get_lora::lora{
2758                            lora,
2759                            alora_invocation_string,
2760                            alora_invocation_tokens,
2761                        });
2762                    }
2763                    queue_results.send(std::move(res));
2764                } break;
2765            case SERVER_TASK_TYPE_SET_LORA:
2766                {
2767                    auto new_loras = construct_lora_list(task.set_lora);
2768                    // logging
2769                    for (size_t i = 0; i < new_loras.size(); ++i) {
2770                        SRV_TRC("set lora adapter idx=%zu scale=%f\n", i, new_loras[i].scale);
2771                    }
2772                    // TODO @ngxson : make lora_adapters a dedicated member of server_context
2773                    params_base.lora_adapters = new_loras;
2774                    auto res = std::make_unique<server_task_result_apply_lora>();
2775                    res->id = task.id;
2776                    queue_results.send(std::move(res));
2777                } break;
2778        }
2779
2780        return true;
2781    }
2782
2783    void iterate(std::vector<server_slot> & slots, std::function<void(server_slot &)> callback) {
2784        for (auto & slot : slots) {
2785            try {
2786                callback(slot);
2787            } catch (const std::exception & e) {
2788                SLT_ERR(slot, "got exception: %s\n", e.what());
2789                send_error(slot, std::string("got exception: ") + e.what(), ERROR_TYPE_SERVER);
2790                slot.release();
2791            }
2792        }
2793    }
2794
2795    void iterate(std::vector<server_slot *> & slots, std::function<void(server_slot &)> callback) {
2796        for (auto & slot : slots) {
2797            try {
2798                callback(*slot);
2799            } catch (const std::exception & e) {
2800                SLT_ERR(*slot, "got exception: %s\n", e.what());
2801                send_error(*slot, std::string("got exception: ") + e.what(), ERROR_TYPE_SERVER);
2802                slot->release();
2803            }
2804        }
2805    }
2806
2807    void abort_all_slots(const std::string & reason) {
2808        for (auto & slot : slots) {
2809            if (slot.is_processing()) {
2810                send_error(slot, reason, ERROR_TYPE_SERVER);
2811                slot.release();
2812            }
2813        }
2814    }
2815
2816    // @ngxson : for debugging only
2817    int64_t t_pre_decode  = 0;
2818    int64_t t_decode      = 0;
2819    int64_t t_post_decode = 0;
2820    int64_t t_sampl       = 0;
2821    int64_t n_pre_decode  = 0;
2822    int64_t n_decode      = 0;
2823    int64_t n_post_decode = 0;
2824    int64_t n_sampl       = 0;
2825// #define DEBUG_TIMINGS
2826#ifdef DEBUG_TIMINGS
2827    struct scoped_timer {
2828        int64_t & t;
2829        int64_t & n;
2830        int64_t t_start;
2831        scoped_timer(int64_t & t_, int64_t & n_) : t(t_), n(n_) {
2832            t_start = ggml_time_us();
2833        }
2834        ~scoped_timer() {
2835            t += ggml_time_us() - t_start;
2836            n++;
2837        }
2838    };
2839#else
2840    struct scoped_timer {
2841        scoped_timer(int64_t &, int64_t &) {}
2842        ~scoped_timer() {}
2843    };
2844#endif
2845
2846    void update_slots() {
2847#ifdef DEBUG_TIMINGS
2848        static int64_t t_prev = 0;
2849        int64_t t_start = ggml_time_us();
2850        if (t_start - t_prev > 5 * 1000 * 1000) { // every 5 seconds
2851            t_prev = t_start;
2852            SRV_INF("n_pre_decode      = %" PRId64 "\n", n_pre_decode);
2853            SRV_INF("avg t_pre_decode  = %f ms\n", (double) t_pre_decode / n_pre_decode / 1000.0);
2854            SRV_INF("avg t_decode      = %f ms\n", (double) t_decode / n_decode / 1000.0);
2855            SRV_INF("avg t_post_decode = %f ms\n", (double) t_post_decode / n_post_decode / 1000.0);
2856            SRV_INF("avg t_sampl       = %f ms\n", (double) t_sampl / n_sampl / 1000.0);
2857        }
2858#endif
2859
2860        // check if all slots are idle
2861        {
2862            bool all_idle = true;
2863
2864            for (auto & slot : slots) {
2865                if (slot.is_processing()) {
2866                    all_idle = false;
2867                    break;
2868                }
2869            }
2870
2871            if (all_idle) {
2872                SRV_TRC("%s", "all slots are idle\n");
2873
2874                metrics_flush_idle();
2875
2876                return; // skip further processing
2877
2878            } else {
2879                SRV_DBG("%s", "posting NEXT_RESPONSE\n");
2880
2881                server_task task(SERVER_TASK_TYPE_NEXT_RESPONSE);
2882                task.id = queue_tasks.get_new_id();
2883                queue_tasks.post(std::move(task));
2884            }
2885        }
2886
2887        try {
2888            scoped_timer t(t_pre_decode, n_pre_decode);
2889            pre_decode();
2890            batch.render();
2891        } catch (const std::exception & e) {
2892            SRV_ERR("pre_decode() failed: %s\n", e.what());
2893            abort_all_slots("pre_decode() failed: " + std::string(e.what()));
2894
2895            // the batch is half-built and not rendered, skip now to avoid UB
2896            return;
2897        }
2898
2899        GGML_ASSERT(batch.slot_batched || batch.size() == 0);
2900
2901        if (batch.slot_batched) {
2902            auto & slot_batched      = batch.slot_batched;
2903            auto & alora_scale       = batch.alora_scale;
2904            auto & alora_disabled_id = batch.alora_disabled_id;
2905
2906            // TODO @ngxson : alora handling is too messy, need to refactor it to be more clear and maintainable
2907            // apply lora, only need to do it once per batch
2908            common_set_adapter_lora(ctx_tgt, slot_batched->lora);
2909
2910            // if the lora is temporarily disabled for an alora, re-enable it
2911            // for next time
2912            if (alora_scale > 0.0f) {
2913                SRV_DBG("re-enabling alora with scale %f\n", alora_scale);
2914                slot_batched->lora[alora_disabled_id].scale = alora_scale;
2915            }
2916
2917            llama_set_embeddings(ctx_tgt, slot_batched->need_embd());
2918        }
2919
2920        llama_batch batch_view;
2921        int32_t off_next = 0;
2922        int32_t n_batch = llama_n_batch(ctx_tgt);
2923        for (int32_t off = 0; off < batch.size(); off = off_next) {
2924            const int32_t n_tokens = std::min(n_batch, batch.size() - off);
2925            try {
2926                scoped_timer t(t_decode, n_decode);
2927                // TODO @ngxson : maybe handle n_batch == 1 here instead of inside decode()
2928
2929                batch_view = batch.get_view(off, n_tokens);
2930                bool ok = decode(n_batch, off, batch_view);
2931#ifdef DEBUG_TIMINGS
2932                llama_synchronize(ctx_tgt);
2933#endif
2934
2935                if (ok) {
2936                    // move the head of the batch forward with the number of tokens we just processed
2937                    off_next = off + n_tokens;
2938
2939                    // on successful decode, restore the original batch size
2940                    n_batch = llama_n_batch(ctx_tgt);
2941                } else {
2942                    // try again with the updated n_batch
2943                    continue;
2944                }
2945            } catch (const std::exception & e) {
2946                SRV_ERR("decode() failed: %s\n", e.what());
2947                abort_all_slots("decode() failed: " + std::string(e.what()));
2948                break; // stop any further processing
2949            }
2950
2951            try {
2952                scoped_timer t(t_post_decode, n_post_decode);
2953                post_decode(n_tokens, off, batch_view);
2954            } catch (const std::exception & e) {
2955                SRV_ERR("post_decode() failed: %s\n", e.what());
2956                abort_all_slots("post_decode() failed: " + std::string(e.what()));
2957                break; // stop any further processing
2958            }
2959        }
2960    }
2961
2962    void pre_decode() {
2963        // apply context-shift if needed
2964        // TODO: simplify and improve
2965        iterate(slots, [&](server_slot & slot) {
2966            if (slot.state == SLOT_STATE_GENERATING && slot.prompt.n_tokens() + 1 >= slot.n_ctx) {
2967                if (!params_base.ctx_shift) {
2968                    // this check is redundant (for good)
2969                    // we should never get here, because generation should already stopped in process_token()
2970                    send_error(slot, "context shift is disabled", ERROR_TYPE_SERVER);
2971                    slot.release();
2972                    return;
2973                }
2974
2975                if (mctx) {
2976                    // we should never reach this because params_base.ctx_shift is automatically disabled if mmproj is loaded
2977                    // we don't support ctx_shift because an image chunk may contains multiple tokens
2978                    GGML_ABORT("not supported by multimodal");
2979                }
2980
2981                if (slot.task->is_parent() || slot.task->is_child()) {
2982                    send_error(slot, "context shift cannot be used for shared prompt", ERROR_TYPE_SERVER);
2983                    slot.release();
2984                    return;
2985                }
2986
2987                // Shift context
2988                int n_keep = slot.task->params.n_keep < 0 ? slot.task->n_tokens() : slot.task->params.n_keep;
2989
2990                if (add_bos_token) {
2991                    n_keep += 1;
2992                }
2993
2994                n_keep = std::min(slot.n_ctx - 4, n_keep);
2995
2996                const int n_left    = slot.prompt.n_tokens() - n_keep;
2997                int       n_discard = slot.task->params.n_discard ? slot.task->params.n_discard : (n_left / 2);
2998
2999                // ref: https://github.com/ggml-org/llama.cpp/pull/24786
3000                n_discard = std::clamp(n_discard, 0, std::max(0, n_left - 1));
3001
3002                SLT_WRN(slot, "slot context shift, n_keep = %d, n_left = %d, n_discard = %d\n", n_keep, n_left, n_discard);
3003
3004                slot.mem.seq_rm (slot.id, n_keep            , n_keep + n_discard);
3005                slot.mem.seq_add(slot.id, n_keep + n_discard, slot.prompt.tokens.pos_next(), -n_discard);
3006
3007                // add generated tokens to cache
3008                // ref: https://github.com/ggml-org/llama.cpp/pull/16818#discussion_r2473269481
3009                {
3010                    GGML_ASSERT(!slot.prompt.tokens.has_mtmd);
3011
3012                    llama_tokens new_tokens = slot.prompt.tokens.get_tokens(); // copy
3013                    for (size_t i = n_keep + n_discard; i < new_tokens.size(); i++) {
3014                        new_tokens[i - n_discard] = new_tokens[i];
3015                    }
3016
3017                    new_tokens.resize(slot.prompt.tokens.size() - n_discard);
3018
3019                    slot.prompt.clear();
3020                    slot.prompt.tokens.insert(new_tokens);
3021                }
3022
3023                slot.truncated = true;
3024            }
3025        });
3026
3027        // start populating the batch for this iteration
3028        batch.clear();
3029
3030        // track if given slot can be batched with slots already in the batch
3031        auto & slot_batched = batch.slot_batched;
3032
3033        std::vector<server_slot *> generating;
3034        std::vector<server_slot *> drafting;
3035
3036        // determine which slots are generating and drafting
3037        iterate(slots, [&](server_slot & slot) {
3038            if (slot.state != SLOT_STATE_GENERATING) {
3039                return;
3040            }
3041
3042            // check if we can batch this slot with the previous one
3043            if (!slot_batched) {
3044                slot_batched = &slot;
3045            } else if (!slot_batched->can_batch_with(slot)) {
3046                return;
3047            }
3048
3049            generating.push_back(&slot);
3050
3051            if (spec) {
3052                common_speculative_get_draft_params(spec.get(), slot.id).drafting = false;
3053
3054                const bool use_ckpt_tgt = ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL;
3055                const bool use_ckpt_dft = ctx_dft_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL;
3056
3057                const int n_draft_max = slot.get_n_draft_max();
3058
3059                if (n_draft_max > 0) {
3060                    GGML_ASSERT(slot.can_speculate());
3061
3062                    if (!slot.spec_draft.empty()) {
3063                        // we have a previous (partial) draft to reuse
3064                        if (use_ckpt_tgt) {
3065                            GGML_ASSERT(!slot.spec_ckpt.empty());
3066                        }
3067                    } else {
3068                        GGML_ASSERT(slot.spec_i_batch.empty());
3069
3070                        slot.spec_ckpt.update_pos(
3071                                slot.prompt.n_tokens(),
3072                                llama_memory_seq_pos_min(llama_get_memory(ctx_tgt), slot.id),
3073                                llama_memory_seq_pos_max(llama_get_memory(ctx_tgt), slot.id));
3074
3075                        if (use_ckpt_dft) {
3076                            slot.spec_ckpt.update_dft(ctx_dft, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
3077                        }
3078
3079                        slot.spec_prompt = slot.prompt.tokens.get_text_tokens();
3080
3081                        common_speculative_get_draft_params(spec.get(), slot.id) = {
3082                            /* .drafting = */ true,
3083                            /* .n_max    = */ n_draft_max,
3084                            /* .pos0     = */ slot.prompt.tokens.pos_next(),
3085                            /* .id_last  = */ slot.sampled,
3086                            /* .prompt   = */ &slot.spec_prompt,
3087                            /* .result   = */ &slot.spec_draft,
3088                        };
3089
3090                        drafting.push_back(&slot);
3091                    }
3092                }
3093            }
3094        });
3095
3096        // generate the actual drafts (if any)
3097        if (!drafting.empty()) {
3098            queue_tasks.yield_to_queue([&]() {
3099                common_speculative_draft(spec.get());
3100            });
3101        }
3102
3103        // make checkpoints if needed
3104        iterate(drafting, [&](server_slot & slot) {
3105            auto & draft = slot.spec_draft;
3106            auto & ckpt  = slot.spec_ckpt;
3107
3108            slot.stats.n_draft_tokens += draft.size();
3109
3110            // TODO: avoid restoring the draft context and re-evaluating the drafted tokens when not needed [TAG_SPEC_AVOID_DRAFT_REEVAL]
3111            const bool use_ckpt_dft = ctx_dft_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL;
3112
3113            if (ctx_dft) {
3114                if (use_ckpt_dft) {
3115                    ckpt.load_dft(ctx_dft, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
3116                }
3117
3118                if (!llama_memory_seq_rm(llama_get_memory(ctx_dft), slot.id, ckpt.pos_max + 1, -1)) {
3119                    GGML_ABORT("failed to remove sequence %d\n", slot.id);
3120                }
3121            }
3122
3123            if (!draft.empty()) {
3124                const bool use_ckpt_tgt =
3125                    ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL ||
3126                   (ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_RS && draft.size() > llama_n_rs_seq(ctx_tgt));
3127
3128                const bool use_ckpt_dft =
3129                   (ctx_dft_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_RS && draft.size() > llama_n_rs_seq(ctx_dft));
3130
3131                if (use_ckpt_tgt) {
3132                    //const int64_t t_start = ggml_time_us();
3133
3134                    ckpt.update_tgt(ctx_tgt, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
3135
3136                    //const int64_t t_total = ggml_time_us() - t_start;
3137                    //printf("checkpoint total: %f ms\n", t_total / 1000.0);
3138
3139                    SLT_DBG(slot, "created speculative checkpoint (pos_min = %d, pos_max = %d, n_tokens = %d, size = %.3f MiB, draft = %.3f MiB)\n",
3140                            ckpt.pos_min, ckpt.pos_max, slot.prompt.n_tokens(),
3141                            (float) ckpt.size() / 1024 / 1024,
3142                            (float) ckpt.data_dft.size() / 1024 / 1024);
3143                }
3144
3145                if (use_ckpt_dft) {
3146                    ckpt.update_dft(ctx_dft, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
3147                }
3148            }
3149        });
3150
3151        // update the batch with the sampled/drafted tokens
3152        iterate(generating, [&](server_slot & slot) {
3153            slot.handle_last_sampled_token(batch);
3154        });
3155
3156        // process in chunks of params.n_batch
3157        int32_t n_batch  = llama_n_batch(ctx_tgt);
3158        int32_t n_ubatch = llama_n_ubatch(ctx_tgt);
3159
3160        auto & alora_scale       = batch.alora_scale;
3161        auto & alora_disabled_id = batch.alora_disabled_id;
3162
3163        // next, batch any pending prompts without exceeding n_batch
3164        if (params_base.cont_batching || batch.size() == 0) {
3165            bool add_ok = true; // false means the batch is full, skip remaining slots
3166
3167            iterate(slots, [&](server_slot & slot) {
3168                if (!add_ok || batch.size() >= n_batch) {
3169                    return; // batch is full, skip remaining slots
3170                }
3171
3172                if (!slot.is_processing()) {
3173                    return;
3174                }
3175
3176                // check if we can batch this slot with the previous one
3177                if (slot_batched && !slot_batched->can_batch_with(slot)) {
3178                    return;
3179                }
3180
3181                // check if this is a child slot
3182                if (slot.state == SLOT_STATE_WAIT_OTHER) {
3183                    SLT_DBG(slot, "%s", "waiting for parent slot to complete\n");
3184                    return;
3185                }
3186
3187                // this slot still has a prompt to be processed
3188                if (slot.state == SLOT_STATE_PROCESSING_PROMPT || slot.state == SLOT_STATE_STARTED) {
3189                    const auto & input_tokens = slot.task->tokens;
3190
3191                    // used to determine the number of tokens added to the batch for the current slot
3192                    const auto n_tokens_prev = batch.size();
3193
3194                    // TODO: maybe move branch to outside of this loop in the future
3195                    if (slot.state == SLOT_STATE_STARTED) {
3196                        slot.stats.update_prompt_start();
3197
3198                        slot.state = SLOT_STATE_PROCESSING_PROMPT;
3199
3200                        SLT_TRC(slot, "new prompt, n_ctx_slot = %d, n_keep = %d, task.n_tokens = %d\n",
3201                                slot.n_ctx, slot.task->params.n_keep, slot.task->n_tokens());
3202
3203                        // print prompt tokens (for debugging)
3204                        /*if (1) {
3205                            // first 16 tokens (avoid flooding logs)
3206                            for (int i = 0; i < std::min<int>(16, input_tokens.size()); i++) {
3207                                SLT_DBG(slot, "prompt token %3d: %6d '%s'\n", i, input_tokens[i], common_token_to_piece(ctx_tgt, input_tokens[i]).c_str());
3208                            }
3209                        } else {
3210                            // all
3211                            for (int i = 0; i < (int) input_tokens.size(); i++) {
3212                                SLT_DBG(slot, "prompt token %3d: %6d '%s'\n", i, input_tokens[i], common_token_to_piece(ctx_tgt, input_tokens[i]).c_str());
3213                            }
3214                        }*/
3215
3216                        // keep track how many tokens we can reuse from the previous state
3217                        int n_past = 0;
3218
3219                        // empty prompt passed -> release the slot and send empty response
3220                        if (input_tokens.empty()) {
3221                            SLT_WRN(slot, "%s", "empty prompt - releasing slot\n");
3222
3223                            slot.print_timings();
3224                            send_final_response(slot);
3225                            slot.release();
3226
3227                            return;
3228                        }
3229
3230                        // TODO: support memory-less logits computation
3231                        if (slot.task->need_logits() && !llama_get_memory(ctx_tgt)) {
3232                            send_error(slot, "the current context does not logits computation. skipping", ERROR_TYPE_SERVER);
3233                            slot.release();
3234                            return;
3235                        }
3236
3237                        if (!slot.can_split()) {
3238                            if (slot.task->n_tokens() > n_ubatch) {
3239                                send_error(slot,
3240                                           string_format(
3241                                               "input (%d tokens) is too large to process. increase the physical batch "
3242                                               "size (current batch size: %d)",
3243                                               slot.task->n_tokens(), n_ubatch),
3244                                           ERROR_TYPE_SERVER);
3245                                slot.release();
3246                                return;
3247                            }
3248
3249                            if (slot.task->n_tokens() > slot.n_ctx) {
3250                                send_error(
3251                                    slot,
3252                                    string_format(
3253                                        "input (%d tokens) is larger than the max context size (%d tokens). skipping",
3254                                        slot.task->n_tokens(), slot.n_ctx),
3255                                    ERROR_TYPE_EXCEED_CONTEXT_SIZE);
3256                                slot.release();
3257                                return;
3258                            }
3259                        } else {
3260                            if (slot.task->n_tokens() >= slot.n_ctx) {
3261                                send_error(slot,
3262                                           string_format("request (%d tokens) exceeds the available context size (%d "
3263                                                         "tokens), try increasing it",
3264                                                         slot.task->n_tokens(), slot.n_ctx),
3265                                           ERROR_TYPE_EXCEED_CONTEXT_SIZE);
3266                                slot.release();
3267                                return;
3268                            }
3269
3270                            if (slot.task->params.cache_prompt) {
3271                                // reuse any previously computed tokens that are common with the new prompt
3272                                n_past = slot.prompt.tokens.get_common_prefix(input_tokens);
3273
3274                                // if there is an alora invoked, don't cache after the invocation start
3275                                if (slot.alora_invocation_start > 0) {
3276                                    SLT_DBG(slot, "only caching to alora invocation start (n_past = %d, alora_invocation_start = %d)\n", n_past, slot.alora_invocation_start);
3277                                    n_past = std::min(n_past, slot.alora_invocation_start - 1);
3278                                }
3279
3280                                const auto n_cache_reuse = slot.task->params.n_cache_reuse;
3281
3282                                const bool can_cache_reuse =
3283                                    llama_memory_can_shift(llama_get_memory(ctx_tgt)) &&
3284                                    !slot.prompt.tokens.has_mtmd;
3285
3286                                if (!can_cache_reuse && n_cache_reuse > 0) {
3287                                    SLT_WRN(slot, "cache reuse is not supported - ignoring n_cache_reuse = %d\n", n_cache_reuse);
3288                                }
3289
3290                                // reuse chunks from the cached prompt by shifting their KV cache in the new position
3291                                if (can_cache_reuse && n_cache_reuse > 0) {
3292                                    GGML_ASSERT(!slot.prompt.tokens.has_mtmd);
3293
3294                                    size_t head_c = n_past; // cache
3295                                    size_t head_p = n_past; // current prompt
3296
3297                                    if (mctx) {
3298                                        // we should never reach this
3299                                        GGML_ABORT("not supported by multimodal");
3300                                    }
3301
3302                                    SLT_DBG(slot, "trying to reuse chunks with size > %d, n_past = %d\n", n_cache_reuse, n_past);
3303
3304                                    while (head_c < slot.prompt.tokens.size() &&
3305                                           head_p < input_tokens.size()) {
3306
3307                                        size_t n_match = 0;
3308                                        while (head_c + n_match < slot.prompt.tokens.size() &&
3309                                               head_p + n_match < input_tokens.size()       &&
3310                                               slot.prompt.tokens[head_c + n_match] == input_tokens[head_p + n_match]) {
3311                                            n_match++;
3312                                        }
3313
3314                                        if (n_match >= (size_t) n_cache_reuse) {
3315                                            SLT_TRC(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);
3316                                            //for (size_t i = head_p; i < head_p + n_match; i++) {
3317                                            //    SLT_DBG(slot, "cache token %3zu: %6d '%s'\n", i, prompt_tokens[i], common_token_to_piece(ctx_tgt, prompt_tokens[i]).c_str());
3318                                            //}
3319
3320                                            const int64_t kv_shift = (int64_t) head_p - (int64_t) head_c;
3321
3322                                            slot.mem.seq_rm (slot.id, head_p, head_c);
3323                                            slot.mem.seq_add(slot.id, head_c, head_c + n_match, kv_shift);
3324
3325                                            for (size_t i = 0; i < n_match; i++) {
3326                                                slot.prompt.tokens.set_token(head_p + i, slot.prompt.tokens[head_c + i]);
3327                                                n_past++;
3328                                            }
3329
3330                                            head_c += n_match;
3331                                            head_p += n_match;
3332                                        } else {
3333                                            head_c += 1;
3334                                        }
3335                                    }
3336
3337                                    SLT_DBG(slot, "after context reuse, new n_past = %d\n", n_past);
3338                                }
3339                            } else {
3340                                // if we don't cache the prompt, we have to remove all previous tokens
3341                                n_past = 0;
3342                            }
3343
3344                            llama_pos pos_next = slot.prompt.tokens.pos_next(n_past);
3345
3346                            // ref: https://github.com/ggml-org/llama.cpp/pull/24110
3347                            const bool has_new_tokens = (n_past < slot.task->n_tokens());
3348
3349                            // the largest pos_min required for a checkpoint to be useful
3350                            const auto pos_min_thold = std::max(0, pos_next - n_swa - (has_new_tokens ? 0 : 1));
3351
3352                            if (n_past > 0 && n_past <= slot.prompt.n_tokens()) {
3353                                const auto pos_min = llama_memory_seq_pos_min(llama_get_memory(ctx_tgt), slot.id);
3354                                if (pos_min == -1) {
3355                                    SLT_ERR(slot, "n_past = %d, slot.prompt.tokens.size() = %d, seq_id = %d, pos_min = %d\n", n_past, (int) slot.prompt.tokens.size(), slot.id, pos_min);
3356                                    GGML_ABORT("pos_min == -1, but n_past > 0 - should not happen: https://github.com/ggml-org/llama.cpp/pull/13833#discussion_r2116181237");
3357                                }
3358
3359                                // when the prompt prefix does not match, print the tokens around the mismatch
3360                                // this is useful for debugging prompt caching
3361                                if (slots_debug) {
3362                                    const int np0 = std::max<int>(n_past - slots_n_diff, 0);
3363                                    const int np1 = std::min<int>(n_past + slots_n_diff + 2, std::min(slot.prompt.tokens.size(), slot.task->tokens.size()));
3364
3365                                    std::stringstream ss0;
3366                                    std::stringstream ss1;
3367
3368                                    std::stringstream st0;
3369                                    std::stringstream st1;
3370
3371                                    ss0 << "old: ... ";
3372                                    ss1 << "new: ... ";
3373
3374                                    for (int i = np0; i < np1; i++) {
3375                                        if (i == n_past) {
3376                                            ss0 << " | ";
3377                                            ss1 << " | ";
3378                                        }
3379
3380                                        {
3381                                            const auto token = slot.prompt.tokens[i];
3382                                            const auto piece = token != LLAMA_TOKEN_NULL ? common_token_to_piece(ctx_tgt, token) : "[mtmd]";
3383                                            ss0 << piece;
3384                                            st0 << std::setw(8) << token;
3385                                        }
3386
3387                                        {
3388                                            const auto token = slot.task->tokens[i];
3389                                            const auto piece = token != LLAMA_TOKEN_NULL ? common_token_to_piece(ctx_tgt, token) : "[mtmd]";
3390                                            ss1 << piece;
3391                                            st1 << std::setw(8) << token;
3392                                        }
3393                                    }
3394
3395                                    SLT_WRN(slot, "%s\n", ss0.str().c_str());
3396                                    SLT_WRN(slot, "%s\n", ss1.str().c_str());
3397
3398                                    SLT_WRN(slot, "%s\n", st0.str().c_str());
3399                                    SLT_WRN(slot, "%s\n", st1.str().c_str());
3400                                }
3401
3402                                if (pos_min >= pos_min_thold) {
3403                                    // search for a context checkpoint
3404                                    const auto it = std::find_if(
3405                                        slot.prompt.checkpoints.rbegin(),
3406                                        slot.prompt.checkpoints.rend(),
3407                                        [&](const auto & cur) {
3408                                            // guarantee that a checkpoint will result in at least one token being processed [TAG_PROMPT_LOGITS]
3409                                            SLT_TRC(slot, "checking checkpoint with [%d, %d] against %d...\n", cur.pos_min, cur.pos_max, pos_min_thold);
3410                                            // workaround for [TAG_CHECKPOINTS_FIX_POS_MIN]
3411                                            if (cur.pos_max > pos_next) {
3412                                                return false;
3413                                            }
3414                                            return cur.pos_min < pos_min_thold || cur.pos_min == 0;
3415                                        }
3416                                    );
3417
3418                                    bool do_reset = it == slot.prompt.checkpoints.rend();
3419
3420                                    if (!do_reset) {
3421                                        // restore the context checkpoint
3422                                        it->load_tgt(ctx_tgt, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
3423                                        it->load_dft(ctx_dft, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
3424                                        // restore the draft's speculative state
3425                                        common_speculative_set_state(spec.get(), slot.id, it->data_spec);
3426
3427                                        pos_next = std::min(pos_next, std::max(it->pos_min + 1, it->pos_max));
3428                                        n_past   = std::min(slot.prompt.tokens.size_up_to_pos(pos_next), (size_t) it->n_tokens);
3429                                        SLT_TRC(slot, "restored context checkpoint (pos_min = %d, pos_max = %d, n_tokens = %" PRId64 ", n_past = %d, size = %.3f MiB)\n", it->pos_min, it->pos_max, it->n_tokens, n_past, (float) it->size() / 1024 / 1024);
3430                                    }
3431
3432                                    if (do_reset) {
3433                                        SLT_TRC(slot, "forcing full prompt re-processing due to lack of cache data (likely due to SWA or hybrid/recurrent memory, see %s)\n",
3434                                                "https://github.com/ggml-org/llama.cpp/pull/13194#issuecomment-2868343055");
3435                                        pos_next = 0;
3436                                        n_past = 0;
3437                                    }
3438                                }
3439                            }
3440
3441                            {
3442                                // erase any checkpoints with pos_max > pos_next
3443                                for (auto it = slot.prompt.checkpoints.begin(); it != slot.prompt.checkpoints.end();) {
3444                                    const auto & cur = *it;
3445                                    if (cur.pos_max > pos_next) {
3446                                        SLT_TRC(slot, "erased invalidated context checkpoint (pos_min = %d, pos_max = %d, n_tokens = %" PRId64 ", n_swa = %d, pos_next = %d, size = %.3f MiB)\n", cur.pos_min, cur.pos_max, cur.n_tokens, n_swa, pos_next, (float) cur.size() / 1024 / 1024);
3447                                        it = slot.prompt.checkpoints.erase(it);
3448                                    } else {
3449                                        ++it;
3450                                    }
3451                                }
3452                            }
3453                        }
3454
3455                        // [TAG_PROMPT_LOGITS]
3456                        if (n_past == slot.task->n_tokens() && n_past > 0) {
3457                            SLT_WRN(slot, "need to evaluate at least 1 token for each active slot (n_past = %d, task.n_tokens() = %d)\n", n_past, slot.task->n_tokens());
3458                            n_past--;
3459                            SLT_WRN(slot, "n_past was set to %d\n", n_past);
3460                        }
3461
3462                        slot.stats.n_prompt_cached    = n_past;
3463                        slot.stats.n_prompt_processed = 0;
3464
3465                        metrics.add_prompt_cached(n_past);
3466
3467                        slot.prompt.tokens.keep_first(n_past);
3468
3469                        // this is to signal the client that the request has started processing
3470                        if (slot.task->params.stream) {
3471                            if (slot.task->params.return_progress) {
3472                                // send initial 0% progress update if needed
3473                                send_partial_response(slot, {}, true);
3474                            } else {
3475                                // otherwise, for streaming without progress, signal HTTP to send the headers (i.e. 200 status)
3476                                send_partial_response(slot, {}, false, true);
3477                            }
3478                        }
3479                    } // end of SLOT_STATE_STARTED
3480
3481                    if (!slot.can_split()) {
3482                        // cannot fit the prompt in the current batch - will try next iter
3483                        if (batch.size() + slot.task->n_tokens() > n_batch) {
3484                            return;
3485                        }
3486                    }
3487
3488                    // note: the prompt timing is advanced in post_decode(), so it does not cover
3489                    //       the tokens added to the batch below
3490                    slot.print_timings_pp();
3491
3492                    // truncate any tokens that are beyond n_past for this slot
3493                    const llama_pos p0 = slot.prompt.tokens.pos_next();
3494
3495                    SLT_TRC(slot, "cached n_tokens = %d, memory_seq_rm [%d, end)\n", slot.prompt.n_tokens(), p0);
3496
3497                    slot.mem.seq_rm(slot.id, p0, -1);
3498
3499                    // If using an alora, there may be uncached tokens that come
3500                    // before the invocation sequence. When this happens, the
3501                    // tokens before the invocation sequence need to be
3502                    // processed without the adapter in a separate batch, then
3503                    // the adapter needs to be enabled for the remaining tokens.
3504                    if (lora_all_alora(slot.lora) && slot.alora_invocation_start - 1 > slot.prompt.n_tokens()) {
3505                        SLT_DBG(slot, "processing pre-alora tokens without the adapter (n_tokens = %d, alora_invocation_start = %d)\n", slot.prompt.n_tokens(), slot.alora_invocation_start);
3506                        const auto & enabled_loras = lora_get_enabled_ids(slot.lora);
3507                        GGML_ASSERT(enabled_loras.size() == 1);
3508                        alora_scale = slot.lora[enabled_loras[0]].scale;
3509                        slot.lora[enabled_loras[0]].scale = 0.0f;
3510                        alora_disabled_id = enabled_loras[0];
3511                    }
3512
3513                    bool do_checkpoint = params_base.n_ctx_checkpoints > 0;
3514
3515                    // make checkpoints only for completion tasks
3516                    do_checkpoint = do_checkpoint && slot.task->type == SERVER_TASK_TYPE_COMPLETION;
3517
3518                    // make a checkpoint of the parts of the memory that cannot be rolled back.
3519                    // checkpoints are created only if:
3520                    // - the model does not support partial sequence removal
3521                    // - the model uses SWA (and we are not using `swa_full`)
3522                    // - the model supports partial sequence removal but only up to a fixed bound
3523                    do_checkpoint = do_checkpoint && (
3524                            ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL ||
3525                            ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_RS ||
3526                            n_swa > 0);
3527
3528                    bool has_mtmd = false;
3529
3530                    // check if we should process the mtmd chunk
3531                    while (true) {
3532                        auto cur_token_idx = slot.prompt.n_tokens();
3533                        if (
3534                            cur_token_idx >= slot.task->n_tokens() ||
3535                            input_tokens[cur_token_idx] != LLAMA_TOKEN_NULL // encountered a text token
3536                        ) {
3537                            break;
3538                        }
3539
3540                        // process the mtmd chunk
3541                        // note: it submits its own decode, potentially be async
3542                        //       so the timing is queued and flushed on the next sync
3543                        metrics_pre_decode();
3544
3545                        // encode on the worker thread, so we can still handle metrics tasks
3546                        size_t n_tokens_out = 0;
3547                        int32_t res = 0;
3548                        queue_tasks.yield_to_queue([&]() {
3549                            res = process_mtmd_chunk(slot, slot.mbatch, cur_token_idx, n_tokens_out);
3550                        });
3551
3552                        if (res != 0) {
3553                            SLT_ERR(slot, "failed to process mtmd chunk, res = %d\n", res);
3554                            send_error(slot, "failed to process mtmd chunk", ERROR_TYPE_SERVER);
3555                            slot.release();
3556                            return; // the slot is done, skip it entirely
3557                        }
3558
3559                        metrics_queue_prompt(n_tokens_out);
3560                        slot.stats.n_prompt_processed += n_tokens_out;
3561                        slot.stats.update_prompt_last();
3562
3563                        // add the mtmd chunk to cache
3564                        {
3565                            const auto & chunk = input_tokens.find_chunk(cur_token_idx);
3566                            // the chunk is already in the KV cache at this point, so we don't need to keep its data around
3567                            slot.prompt.tokens.push_back_placeholder(chunk.get());
3568                        }
3569
3570                        has_mtmd = true;
3571                    }
3572
3573                    const auto & spans = slot.task->params.message_spans;
3574                    const auto last_user_pos = spans.last_user_message_pos();
3575
3576                    // add prompt tokens for processing in the current batch
3577                    while (slot.prompt.n_tokens() < slot.task->n_tokens() && batch.size() < n_batch) {
3578                        // get next token to process
3579                        llama_token cur_tok = input_tokens[slot.prompt.n_tokens()];
3580                        if (cur_tok == LLAMA_TOKEN_NULL) {
3581                            break; // end of text chunk
3582                        }
3583
3584                        // if this is an alora request with pre-invocation
3585                        // tokens that are not cached, we need to stop filling
3586                        // this batch at those pre-invocation tokens.
3587                        if (alora_scale > 0 && slot.prompt.n_tokens() == slot.alora_invocation_start - 1) {
3588                            SLT_DBG(slot, "stop prompt batch filling at (n_tokens = %d, alora_invocation_start = %d)\n", slot.prompt.n_tokens(), slot.alora_invocation_start);
3589                            break;
3590                        }
3591
3592                        // embedding requires all tokens in the batch to be output;
3593                        // MTP also wants logits at every prompt position so the
3594                        // streaming hook can mirror t_h_nextn into ctx_dft.
3595                        add_ok &= batch.add(slot.id,
3596                            cur_tok,
3597                            /* pos       = */ slot.prompt.tokens.pos_next(),
3598                            /* output    = */ slot.need_embd(),
3599                            /* is_prompt = */ true);
3600                        slot.prompt.tokens.push_back(cur_tok);
3601
3602                        // break at the last user message, or at user messages at least min step past the last checkpoint
3603                        if (do_checkpoint && spans.is_user_start(slot.prompt.n_tokens())) {
3604                            const auto pos = slot.prompt.n_tokens();
3605                            const auto & checkpoints = slot.prompt.checkpoints;
3606
3607                            if (pos == last_user_pos || checkpoints.empty() || pos > checkpoints.back().n_tokens + params_base.checkpoint_min_step) {
3608                                break;
3609                            }
3610                        }
3611
3612                        // process the last few tokens of the prompt separately in order to allow for a checkpoint to be created.
3613                        // create checkpoints that many tokens before the end of the prompt:
3614                        //  - 4 + n_ubatch
3615                        //  - 4
3616                        // ref: https://github.com/ggml-org/llama.cpp/pull/20288
3617                        if (do_checkpoint) {
3618                            static const int checkpoint_offsets[] = {4 + n_ubatch, 4};
3619
3620                            bool should_break = false;
3621                            for (int offset : checkpoint_offsets) {
3622                                const int n_last = std::min(n_batch, offset);
3623                                if (slot.task->n_tokens() == slot.prompt.n_tokens() + n_last) {
3624                                    should_break = true;
3625                                    break;
3626                                }
3627                            }
3628                            if (should_break) {
3629                                break;
3630                            }
3631                        }
3632                    }
3633
3634                    // the number of tokens added to the batch for the current slot
3635                    const auto n_tokens_cur = batch.size() - n_tokens_prev;
3636
3637                    const auto n_tokens_start = slot.prompt.n_tokens() - n_tokens_cur;
3638
3639                    const bool near_prompt_end = slot.task->n_tokens() < slot.prompt.n_tokens() + n_ubatch;
3640
3641                    const bool is_user_start = spans.is_user_start(n_tokens_start);
3642                    const bool is_last_user_message = n_tokens_start == last_user_pos;
3643
3644                    // entire prompt has been processed
3645                    if (slot.prompt.n_tokens() == slot.task->n_tokens()) {
3646                        slot.state = SLOT_STATE_DONE_PROMPT;
3647
3648                        GGML_ASSERT(batch.size() > 0);
3649
3650                        // extract the logits only for the last token
3651                        batch.set_output(batch.size() - 1, true);
3652
3653                        slot.stats.n_gen = 0;
3654                        slot.i_batch     = batch.size() - 1;
3655
3656                        if (!encoder_decoder) {
3657                            slot.init_sampler();
3658                        }
3659                    } else {
3660                        // skip ordinary mid-prompt checkpoints, unless the batch starts a user
3661                        // message or we are near the end of the prompt
3662                        if (!is_user_start && !near_prompt_end) {
3663                            do_checkpoint = false;
3664                        }
3665                    }
3666
3667                    const auto pos_min = llama_memory_seq_pos_min(llama_get_memory(ctx_tgt), slot.id);
3668                    const auto pos_max = llama_memory_seq_pos_max(llama_get_memory(ctx_tgt), slot.id);
3669
3670                    // nothing to checkpoint yet
3671                    // TODO: is this check needed?
3672                    if (do_checkpoint && pos_min < 0) {
3673                        do_checkpoint = false;
3674                    }
3675
3676                    // do not checkpoint after mtmd chunks
3677                    do_checkpoint = do_checkpoint && !has_mtmd;
3678
3679                    // no need to create checkpoints that are too close together, unless it's the last user message
3680                    do_checkpoint = do_checkpoint && (
3681                            slot.prompt.checkpoints.empty() ||
3682                            is_last_user_message || near_prompt_end ||
3683                            n_tokens_start > slot.prompt.checkpoints.back().n_tokens + params_base.checkpoint_min_step);
3684                    SLT_DBG(slot, "main/do_checkpoint = %s, pos_min = %d, pos_max = %d\n", do_checkpoint ? "yes" : "no", pos_min, pos_max);
3685
3686                    // note: we create the checkpoint before calling llama_decode(), so the current batch is not
3687                    //       yet processed and therefore it is not part of the checkpoint.
3688                    if (do_checkpoint) {
3689                        create_checkpoint(slot, n_tokens_cur, pos_min, pos_max);
3690                    }
3691                }
3692
3693                if (!slot_batched) {
3694                    slot_batched = &slot;
3695                }
3696            });
3697        }
3698    }
3699
3700    // returns true = success ; false = retry with smaller batch size
3701    // throw std::runtime_error on fatal error
3702    bool decode(int32_t & n_batch, int32_t off, llama_batch & batch_view) {
3703        SRV_DBG("n_batch (effective) = %d, off = %d\n", n_batch, off);
3704
3705        metrics_pre_decode();
3706
3707        if (batch.size() == 0) {
3708            SRV_WRN("%s", "no tokens to decode\n");
3709
3710            if (++n_empty_consecutive > 3) {
3711                GGML_ABORT("fatal error - please provide logs and repro in %s\n", "https://github.com/ggml-org/llama.cpp/pull/20277");
3712            }
3713
3714            return true; // nothing to decode
3715        } else {
3716            n_empty_consecutive = 0;
3717        }
3718
3719        // TODO @ngxson : dft model may have different n_embd than the tgt model, so we check & reject if that's the case
3720        // this case is not currently used by any models, but may need to be supported in the future
3721        if (spec && batch.has_embd) {
3722            if (llama_model_n_embd_inp(model_dft) != llama_model_n_embd_inp(model_tgt)) {
3723                SRV_ERR("%s", "unsupported batch.has_embd + spec case\n");
3724                throw std::runtime_error("unsupported batch.has_embd + spec case");
3725            }
3726        }
3727
3728        bool has_output = false;
3729        for (int i = off; i < off + batch_view.n_tokens; ++i) {
3730            has_output |= batch.tokens[i].output;
3731        }
3732
3733        // yield to the queue, so we can still handle metrics tasks while decoding
3734        // note: the sync is done here too, so that the wait is also covered by the yield
3735        int ret = 0;
3736        queue_tasks.yield_to_queue([&]() {
3737            if (encoder_decoder && slots[0].state == SLOT_STATE_DONE_PROMPT) {
3738                auto & slot = slots[0];
3739                auto encoder_batch = batch_view;
3740                encoder_batch.logits = nullptr;
3741                ret = llama_encode(ctx_tgt, encoder_batch);
3742                if (ret != 0) {
3743                    throw std::runtime_error("failed to encode prompt");
3744                }
3745
3746                // The encoder prompt is not part of the decoder's KV history.
3747                slot.prompt.clear();
3748                slot.prompt.tokens.insert(decoder_prefix);
3749                slot.init_sampler();
3750                // Quantized MoE routing is sensitive to batching; keep prefix evaluation incremental.
3751                for (auto & token : decoder_prefix) {
3752                    auto decoder_batch = llama_batch_get_one(&token, 1);
3753                    ret = llama_decode(ctx_tgt, decoder_batch);
3754                    if (ret != 0) {
3755                        break;
3756                    }
3757                }
3758                slot.i_batch = off;
3759            } else {
3760                ret = llama_decode(ctx_tgt, batch_view);
3761            }
3762            if (ret == 0 && has_output) {
3763                llama_synchronize(ctx_tgt);
3764            }
3765        });
3766
3767        if (ret != 0) {
3768            {
3769                std::string err;
3770
3771                if (n_batch == 1 && ret == 1) {
3772                    // TODO: try to terminate only the largest active slot/sequence and continue with the rest
3773                    //       need to remove the tokens from the current batch too
3774                    err = "Context size has been exceeded.";
3775                }
3776
3777                if (ret == -1) {
3778                    err = "Invalid input batch.";
3779                }
3780
3781                if (ret < -1) {
3782                    // TODO: update slot state based on llama_memory_seq_pos_min() and llama_memory_seq_pos_max()
3783                    err = "Compute error.";
3784                }
3785
3786                // TODO: handle ret == 2 (abort) when we start aborting
3787
3788                if (!err.empty()) {
3789                    SRV_ERR("%s off = %d, n_batch = %d, ret = %d\n", err.c_str(), off, n_batch, ret);
3790
3791                    for (auto & slot : slots) {
3792                        if (slot.is_processing()) {
3793                            send_error(slot, err);
3794                            slot.release();
3795
3796                            // note: it's complicated to keep track of how much of the current batch has been
3797                            //       processed before the error occurred, so we simply clear the entire context
3798                            slot.prompt_clear();
3799                        }
3800                    }
3801
3802                    // stop, do not retry with smaller batch size
3803                    throw std::runtime_error(err);
3804                }
3805            }
3806
3807            // retry with half the batch size to try to find a free slot in the KV cache
3808            if (!try_clear_idle_slots()) {
3809                n_batch /= 2;
3810            }
3811
3812            SRV_WRN("failed to find free space in the KV cache, retrying with smaller batch size, off = %d, n_batch = %d, ret = %d\n", off, n_batch, ret);
3813
3814            return false; // retry with the updated n_batch
3815        } else {
3816            // success, apply batch metrics
3817            metrics_post_decode(off, batch_view.n_tokens, has_output);
3818        }
3819
3820        // TODO: avoid restoring the draft context and re-evaluating the drafted tokens when not needed [TAG_SPEC_AVOID_DRAFT_REEVAL]
3821        //       for now, always re-evaluate for simplicity
3822        //       ref: https://github.com/ggml-org/llama.cpp/pull/22728#issuecomment-4400925384
3823        if (spec) {
3824            bool ok = true;
3825            queue_tasks.yield_to_queue([&]() {
3826                ok = common_speculative_process(spec.get(), batch_view);
3827            });
3828
3829            if (!ok) {
3830                SRV_ERR("%s", "failed to process speculative batch\n");
3831
3832                // TODO: handle error
3833                throw std::runtime_error("failed to process speculative batch");
3834            }
3835        }
3836
3837        // handle `n_cmpl > 1` tasks - when the main prompt is processed, activate all child tasks too
3838        for (auto & slot : slots) {
3839            if (slot.state == SLOT_STATE_DONE_PROMPT && slot.task->is_parent()) {
3840                std::vector<server_slot *> children;
3841                for (auto & other : slots) {
3842                    if (other.state == SLOT_STATE_WAIT_OTHER && slot.task->id == other.task->id_parent) {
3843                        children.push_back(&other);
3844                    }
3845                }
3846
3847                // all children slots should already launched by launch_slots_with_parent_task()
3848                // copy state to the child slots
3849                for (auto & child : children) {
3850                    SLT_TRC(slot, " - copying state to child %d\n", child->id);
3851
3852                    GGML_ASSERT(child->state == SLOT_STATE_WAIT_OTHER);
3853
3854                    slot.copy_state_to(*child);
3855                    child->state = SLOT_STATE_DONE_PROMPT;
3856                }
3857            }
3858        }
3859
3860        return true;
3861    }
3862
3863    void post_decode(int32_t n_batch_tokens, int32_t off, llama_batch & batch_view) {
3864        // for checking if a given batch index is inside batch_view
3865        auto is_inside_view = [&](int32_t idx) {
3866            return idx >= off && idx < off + n_batch_tokens;
3867        };
3868
3869        // TODO @ngxson : it's tricky to make sub-batch compatible with common_sampler_sample_and_accept_n,
3870        // so for now we will throw an error in this case: https://github.com/ggml-org/llama.cpp/issues/24840
3871        iterate(slots, [&](server_slot & slot) {
3872            for (auto & i : slot.spec_i_batch) {
3873                if (!is_inside_view(i)) {
3874                    throw std::runtime_error(string_format("speculative batch index %d is not inside the current sub-batch [%d, %d)", i, off, off + n_batch_tokens));
3875                }
3876            }
3877        });
3878
3879        auto accept_special_token = [&](server_slot & slot, llama_token token) {
3880            return params_base.special ||
3881                slot.task->params.sampling.preserved_tokens.find(token) != slot.task->params.sampling.preserved_tokens.end();
3882        };
3883
3884        iterate(slots, [&](server_slot & slot) {
3885            // optionally send prompt processing progress
3886            if (slot.state == SLOT_STATE_PROCESSING_PROMPT || slot.state == SLOT_STATE_DONE_PROMPT) {
3887                if (slot.task->params.stream && slot.task->params.return_progress) {
3888                    send_partial_response(slot, {}, true);
3889                }
3890            }
3891
3892            if (!is_inside_view(slot.i_batch)) {
3893                // the required token not in this sub-batch, skip
3894                return;
3895            }
3896
3897            if (slot.state == SLOT_STATE_DONE_PROMPT) {
3898                if (slot.task->type == SERVER_TASK_TYPE_EMBEDDING) {
3899                    // prompt evaluated for embedding
3900                    send_embedding(slot, batch_view);
3901                    slot.release();
3902                    slot.i_batch = -1;
3903                    return;
3904                }
3905
3906                if (slot.task->type == SERVER_TASK_TYPE_RERANK) {
3907                    send_rerank(slot, batch_view);
3908                    slot.release();
3909                    slot.i_batch = -1;
3910                    return;
3911                }
3912
3913                GGML_ASSERT(slot.task->need_sampling());
3914
3915                // prompt evaluated for next-token prediction
3916                slot.state = SLOT_STATE_GENERATING;
3917
3918                if (slot.can_speculate()) {
3919                    common_speculative_begin(spec.get(), slot.id, slot.prompt.tokens.get_text_tokens());
3920                }
3921            } else if (slot.state != SLOT_STATE_GENERATING) {
3922                return;
3923            }
3924
3925            if (slot.can_speculate() && !slot.spec_draft.empty()) {
3926                return; // sample using speculative decoding
3927            }
3928
3929            // shifted according to the current sub-batch
3930            const int tok_idx = slot.i_batch - off;
3931
3932            llama_token id;
3933            {
3934                scoped_timer timer(t_sampl, n_sampl);
3935                id = common_sampler_sample(slot.smpl.get(), slot.ctx_tgt, tok_idx);
3936            }
3937
3938            slot.i_batch = -1;
3939
3940            common_sampler_accept(slot.smpl.get(), id, true);
3941
3942            // here we have synchronized the llama_context (due to the sampling above), so we can do time measurement
3943            const int64_t t_now = ggml_time_us();
3944
3945            slot.stats.n_gen += 1;
3946
3947            if (slot.stats.n_gen == 1) {
3948                slot.stats.update_prompt_last();
3949                slot.t_print_last = t_now;
3950                slot.n_gen_last = 0;
3951            }
3952
3953            slot.stats.update_gen_last();
3954
3955            completion_token_output result;
3956            result.tok          = id;
3957            result.text_to_send = common_token_to_piece(slot.ctx_tgt, result.tok, accept_special_token(slot, result.tok));
3958            result.prob         = 1.0f; // TODO: set it here instead of doing inside populate_token_probs
3959
3960            if (slot.task->params.sampling.n_probs > 0) {
3961                populate_token_probs(slot, result, slot.task->params.post_sampling_probs, params_base.special, tok_idx);
3962            }
3963
3964            if (!process_token(result, slot)) {
3965                // release slot because of stop condition
3966                slot.print_timings();
3967                send_final_response(slot);
3968                slot.release();
3969
3970                return;
3971            }
3972
3973            slot.print_timings_tg();
3974        });
3975
3976        // speculative decoding - main model sample and accept
3977        iterate(slots, [&](server_slot & slot) {
3978            if (slot.state != SLOT_STATE_GENERATING || !slot.can_speculate() ||
3979                    slot.spec_draft.empty() || slot.spec_i_batch.empty()) {
3980                return;
3981            }
3982
3983            // save the original draft size
3984            const size_t n_draft = slot.spec_draft.size();
3985
3986            GGML_ASSERT(n_draft > 0);
3987
3988            // verify and try to accept the draft
3989            {
3990                common_sampler_ptr smpl_save(common_sampler_clone(slot.smpl.get()));
3991
3992                GGML_ASSERT(slot.spec_i_batch.size() == n_draft + 1);
3993                const auto & synth_probs = common_speculative_get_synth_probs(spec.get());
3994                auto accepted = synth_probs.empty()
3995                    ? common_sampler_sample_and_accept_n(slot.smpl.get(), slot.ctx_tgt, slot.spec_i_batch, slot.spec_draft)
3996                    : server_sample_and_accept_synth(
3997                            slot.smpl.get(), slot.ctx_tgt, slot.spec_i_batch, slot.spec_draft,
3998                            synth_probs, slot.spec_synth_rng, slot.spec_is_replay);
3999                slot.spec_i_batch.clear();
4000
4001                GGML_ASSERT(accepted.size() >= 1);
4002
4003                const uint32_t n_rollback = slot.spec_draft.size() + 1 - accepted.size();
4004
4005                const bool use_ckpt_tgt =
4006                    ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_FULL ||
4007                    (ctx_tgt_seq_rm_type == COMMON_CONTEXT_SEQ_RM_TYPE_RS && n_rollback > llama_n_rs_seq(ctx_tgt));
4008
4009                // check for partial draft acceptance
4010                if (n_rollback > 0) {
4011                    if (use_ckpt_tgt) {
4012                        if (trace > 0) {
4013                            SLT_INF(slot, "accepted %2zu/%2zu draft tokens (restore checkpoint)\n", accepted.size() - 1, slot.spec_draft.size());
4014                        }
4015
4016                        // partial acceptance is not supported by the context -> truncate the draft and restore the state
4017                        slot.spec_is_replay = true;
4018                        slot.spec_draft = std::move(accepted);
4019
4020                        const auto & ckpt = slot.spec_ckpt;
4021
4022                        SLT_DBG(slot, "restoring speculative checkpoint (pos_min = %d, pos_max = %d, size = %zu)\n", ckpt.pos_min, ckpt.pos_max, ckpt.size());
4023
4024                        ckpt.load_tgt(slot.ctx_tgt, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
4025
4026                        if (slot.ctx_dft) {
4027                            ckpt.load_dft(slot.ctx_dft, slot.id, LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY);
4028                        }
4029
4030                        slot.mem.seq_rm(slot.id, ckpt.pos_max + 1, -1);
4031
4032                        slot.prompt.tokens.keep_first(ckpt.n_tokens);
4033                        common_sampler_copy(smpl_save.get(), slot.smpl.get());
4034
4035                        return;
4036                    }
4037                }
4038
4039                if (trace > 0) {
4040                    SLT_INF(slot, "accepted %2zu/%2zu draft tokens\n", accepted.size() - 1, n_draft);
4041                }
4042
4043                common_speculative_accept(spec.get(), slot.id, accepted.size() - 1);
4044
4045                slot.spec_draft = std::move(accepted);
4046            }
4047
4048            const auto ids = std::move(slot.spec_draft);
4049
4050            size_t n_accepted = ids.size() - 1;
4051            if (slot.spec_is_replay && n_accepted > 0) {
4052                n_accepted--;
4053            }
4054            slot.spec_is_replay = false;
4055
4056            slot.stats.update_gen_last();
4057
4058            // update how many tokens out of those tested were accepted
4059            slot.stats.n_draft_accepted += n_accepted;
4060            slot.stats.n_draft_verif_steps += 1;
4061
4062            auto & n_accepted_per_pos = slot.n_accepted_per_pos;
4063            if (n_accepted_per_pos.empty()) {
4064                n_accepted_per_pos.resize(common_speculative_n_max(spec.get()), 0);
4065            }
4066            for (size_t i = 0; i < n_accepted && i < n_accepted_per_pos.size(); ++i) {
4067                n_accepted_per_pos[i]++;
4068            }
4069
4070            // add accepted tokens to the prompt
4071            slot.prompt.tokens.keep_first(slot.prompt.n_tokens() - n_draft);
4072            slot.prompt.tokens.insert({ids.begin(), ids.end() - 1});
4073
4074            slot.sampled = ids.back(); // last accepted token
4075            SLT_DBG(slot, "add accepted tokens: sampled=%d, ids.size=%zu, n_draft=%zu\n", slot.sampled, ids.size(), n_draft);
4076
4077            slot.mem.seq_rm(slot.id, slot.prompt.tokens.pos_next(), -1);
4078
4079            for (size_t i = 0; i < ids.size(); ++i) {
4080                completion_token_output result;
4081
4082                result.tok          = ids[i];
4083                result.text_to_send = common_token_to_piece(slot.ctx_tgt, result.tok, accept_special_token(slot, result.tok));
4084                result.prob         = 1.0f; // set later
4085
4086                // TODO: set result.probs
4087
4088                slot.stats.n_gen += 1;
4089
4090                if (!process_token(result, slot)) {
4091                    slot.print_timings();
4092                    send_final_response(slot);
4093                    slot.release();
4094
4095                    return;
4096                }
4097            }
4098
4099            slot.print_timings_tg();
4100
4101            SLT_DBG(slot, "accepted %d/%d draft tokens, new n_tokens = %d\n", (int) n_accepted, (int) n_draft, slot.prompt.n_tokens());
4102        });
4103    }
4104
4105    // context size of a single slot, capped by --kv-unified-per-slot and by the training context of the model
4106    int n_ctx_slot() const {
4107        int res = llama_n_ctx_seq(ctx_tgt);
4108
4109        if (params_base.kv_unified_per_slot > 0) {
4110            res = std::min(res, params_base.kv_unified_per_slot);
4111        }
4112
4113        return std::min(res, llama_model_n_ctx_train(model_tgt));
4114    }
4115
4116    server_response_reader get_response_reader() {
4117        return server_response_reader(queue_tasks, queue_results, HTTP_POLLING_SECONDS);
4118    }
4119
4120    //
4121    // metrics helpers
4122    //
4123
4124    // call before submitting a decode, so that the queued prompt stats can be timed
4125    void metrics_pre_decode() {
4126        t_decode_start = ggml_time_us();
4127    }
4128
4129    // the batch is submitted, but its compute may not be done yet
4130    void metrics_queue_prompt(uint64_t n_tokens) {
4131        if (n_tokens == 0) {
4132            return;
4133        }
4134        if (n_prompt_queued == 0) {
4135            t_prompt_start = t_decode_start;
4136        }
4137        n_prompt_queued += n_tokens;
4138    }
4139
4140    // call only after the context is synchronized, otherwise the time is meaningless
4141    void metrics_flush_prompt() {
4142        if (n_prompt_queued == 0) {
4143            return;
4144        }
4145        metrics.add_prompt(n_prompt_queued, ggml_time_us() - t_prompt_start);
4146        n_prompt_queued = 0;
4147    }
4148
4149    // has_output is computed by the caller, which also already synchronized the context if it is set
4150    void metrics_post_decode(int32_t off, int32_t n_tokens, bool has_output) {
4151        metrics.n_decode++;
4152        for (const auto & slot : slots) {
4153            if (slot.is_processing()) {
4154                metrics.n_busy_slots++;
4155            }
4156            metrics.n_tokens_max = std::max(metrics.n_tokens_max, (uint64_t) slot.prompt.n_tokens());
4157        }
4158
4159        // apply enqueued prompt tokens stats
4160        // note: a slot can be released before we get here, which clears its stats
4161        //       the tokens were still computed, counted in the global metrics, not in slot
4162        uint64_t n_prompt_tokens = 0;
4163
4164        for (int i = off; i < off + n_tokens; ++i) {
4165            const auto & t = batch.tokens[i];
4166
4167            if (!t.is_prompt) {
4168                continue; // generated tokens are handled after sampling
4169            }
4170
4171            n_prompt_tokens++;
4172
4173            auto & slot = slots[t.id_slot];
4174            if (slot.stats.is_set()) {
4175                slot.stats.n_prompt_processed++;
4176            }
4177        }
4178
4179        metrics_queue_prompt(n_prompt_tokens);
4180
4181        if (has_output) {
4182            // the context is already synchronized, so the timings are correct
4183            metrics_flush_prompt();
4184        }
4185
4186        // advance the prompt timing of the slots that had tokens in this batch
4187        // note: a second pass, it must run after the sync to reflect the compute
4188        const int64_t t_now = ggml_time_us();
4189        for (int i = off; i < off + n_tokens; ++i) {
4190            const auto & t = batch.tokens[i];
4191            auto & slot = slots[t.id_slot];
4192            if (t.is_prompt && slot.stats.is_set()) {
4193                slot.stats.set_prompt_last(t_now);
4194            }
4195        }
4196    }
4197
4198    // flush any queued prompt metrics if all slots are now idle
4199    void metrics_flush_idle() {
4200        if (n_prompt_queued == 0) {
4201            return;
4202        }
4203
4204        llama_synchronize(ctx_tgt);
4205        metrics_flush_prompt();
4206    }
4207
4208    void metrics_on_prediction(const server_slot & slot) {
4209        const uint64_t t_us    = slot.stats.t_gen_us();
4210        const uint64_t n       = slot.stats.n_gen;
4211        const uint64_t n_steps = slot.stats.n_gen_steps();
4212
4213        metrics.predict       .add(n, n_steps, t_us);
4214        metrics.predict_bucket.add(n, n_steps, t_us);
4215
4216        metrics.n_draft_tokens      += slot.stats.n_draft_tokens;
4217        metrics.n_draft_accepted    += slot.stats.n_draft_accepted;
4218        metrics.n_draft_verif_steps += slot.stats.n_draft_verif_steps;
4219
4220        auto & dst = metrics.n_accepted_per_pos;
4221        const auto & src = slot.n_accepted_per_pos;
4222
4223        if (dst.size() < src.size()) {
4224            dst.resize(src.size(), 0);
4225        }
4226        for (size_t i = 0; i < src.size(); i++) {
4227            dst[i] += src[i];
4228        }
4229    }
4230};
4231
4232//
4233// server_context (public API)
4234//
4235
4236server_context::server_context() : impl(new server_context_impl()) {}
4237server_context::~server_context() = default;
4238
4239bool server_context::load_model(common_params & params) {
4240    return impl->load_model(params);
4241}
4242
4243void server_context::start_loop() {
4244    auto & params = impl->params_base;
4245    impl->queue_tasks.start_loop(params.sleep_idle_seconds * 1000);
4246}
4247
4248void server_context::terminate() {
4249    impl->queue_tasks.terminate();
4250}
4251
4252llama_context * server_context::get_llama_context() const {
4253    return impl->ctx_tgt;
4254}
4255
4256server_response_reader server_context::get_response_reader() {
4257    return impl->get_response_reader();
4258}
4259
4260server_context_meta server_context::get_meta() const {
4261    auto bos_id = llama_vocab_bos(impl->vocab);
4262    auto eos_id = llama_vocab_eos(impl->vocab);
4263    auto bos_token_str = bos_id != LLAMA_TOKEN_NULL ? common_token_to_piece(impl->ctx_tgt, bos_id, true) : "";
4264    auto eos_token_str = eos_id != LLAMA_TOKEN_NULL ? common_token_to_piece(impl->ctx_tgt, eos_id, true) : "";
4265
4266    const char * ftype_name = llama_ftype_name(llama_model_ftype(impl->model_tgt));
4267
4268    return server_context_meta {
4269        /* build_info             */ std::string(llama_build_info()),
4270        /* model_name             */ impl->model_name,
4271        /* model_aliases          */ impl->model_aliases,
4272        /* model_tags             */ impl->model_tags,
4273        /* model_path             */ impl->params_base.model.path,
4274        /* has_mtmd               */ impl->mctx != nullptr,
4275        /* has_inp_image          */ impl->chat_params.allow_image,
4276        /* has_inp_audio          */ impl->chat_params.allow_audio,
4277        /* has_inp_video          */ impl->chat_params.allow_video,
4278        /* json_ui_settings       */ impl->json_ui_settings,
4279        /* slot_n_ctx             */ impl->n_ctx_slot(),
4280        /* pooling_type           */ llama_pooling_type(impl->ctx_tgt),
4281
4282        /* chat_params            */ impl->chat_params,
4283        /* chat_template_caps     */ common_chat_templates_get_caps(impl->chat_params.tmpls.get()),
4284
4285        /* bos_token_str          */ bos_token_str,
4286        /* eos_token_str          */ eos_token_str,
4287        /* fim_pre_token          */ llama_vocab_fim_pre(impl->vocab),
4288        /* fim_sub_token          */ llama_vocab_fim_suf(impl->vocab),
4289        /* fim_mid_token          */ llama_vocab_fim_mid(impl->vocab),
4290        /* fim_pad_token          */ llama_vocab_fim_pad(impl->vocab),
4291        /* fim_rep_token          */ llama_vocab_fim_rep(impl->vocab),
4292        /* fim_sep_token          */ llama_vocab_fim_sep(impl->vocab),
4293
4294        /* logit_bias_eog         */ impl->params_base.sampling.logit_bias_eog,
4295
4296        /* model_vocab_type       */ llama_vocab_type(impl->vocab),
4297        /* model_vocab_n_tokens   */ llama_vocab_n_tokens(impl->vocab),
4298        /* model_n_ctx_train      */ llama_model_n_ctx_train(impl->model_tgt),
4299        /* model_n_embd_inp       */ llama_model_n_embd(impl->model_tgt),
4300        /* model_n_params         */ llama_model_n_params(impl->model_tgt),
4301        /* model_size             */ llama_model_size(impl->model_tgt),
4302        /* model_ftype            */ ftype_name,
4303    };
4304}
4305
4306// generator-like API for HTTP response generation
4307// may have bypass_sleep = true if the task does not use ctx_server
4308struct server_res_generator : server_res_spipe {
4309    server_response_reader rd;
4310    server_res_generator(server_queue & queue_tasks, server_response & queue_results, int sleep_idle_seconds, bool bypass_sleep = false)
4311            : rd(queue_tasks, queue_results, HTTP_POLLING_SECONDS) {
4312        // fast path in case sleeping is disabled
4313        bypass_sleep |= sleep_idle_seconds < 0;
4314        if (!bypass_sleep) {
4315            queue_tasks.wait_until_no_sleep();
4316        }
4317    }
4318    void ok(const json & response_data) {
4319        status = 200;
4320        data = safe_json_to_str(response_data);
4321    }
4322    void error(const json & error_data) {
4323        status = json_value(error_data, "code", 500);
4324        data = safe_json_to_str({{ "error", error_data }});
4325    }
4326};
4327
4328void server_context::set_state_callback(server_state_callback_t callback) {
4329    impl->callback_state = std::move(callback);
4330}
4331
4332//
4333// server_routes
4334//
4335
4336std::unique_ptr<server_res_generator> server_routes::handle_completions_impl(
4337            const server_http_req & req,
4338            server_task_type type,
4339            const json & data,
4340            const std::vector<raw_buffer> & files,
4341            task_response_type res_type) {
4342    GGML_ASSERT(type == SERVER_TASK_TYPE_COMPLETION || type == SERVER_TASK_TYPE_INFILL);
4343
4344    auto res = create_response();
4345    auto completion_id = gen_chatcmplid();
4346    auto & rd = res->rd;
4347    auto & params = this->params;
4348
4349    res->set_req(&req); // will also set spipe if needed
4350
4351    int32_t sse_ping_interval = params.sse_ping_interval;
4352
4353    try {
4354        std::vector<server_task> tasks;
4355
4356        const auto & prompt = data.at("prompt");
4357        // TODO: this log can become very long, put it behind a flag or think about a more compact format
4358        //SRV_DBG("Prompt: %s\n", prompt.is_string() ? prompt.get<std::string>().c_str() : prompt.dump(2).c_str());
4359
4360        if (!params.path_prompts_log_dir.empty()) {
4361            const auto file_path = std::filesystem::path(params.path_prompts_log_dir) / string_format("%012" PRId64 ".txt", ggml_time_ms());
4362            std::ofstream f(file_path);
4363            if (f) {
4364                f << (prompt.is_string() ? prompt.get<std::string>().c_str() : prompt.dump(2).c_str());
4365            } else {
4366                SRV_ERR("failed to create %s\n", file_path.string().c_str());
4367            }
4368        }
4369
4370        // process prompt
4371        std::vector<server_tokens> inputs;
4372
4373        if (res_type != TASK_RESPONSE_TYPE_NONE && ctx_server.mctx != nullptr) {
4374            // This is the case used by OAI compatible chat path with MTMD. TODO It can be moved to the path below.
4375            inputs.push_back(process_mtmd_prompt(ctx_server.mctx, prompt.get<std::string>(), files, ctx_server.init_opt));
4376        } else {
4377            // Everything else, including multimodal completions.
4378            inputs = tokenize_input_prompts(ctx_server.vocab, ctx_server.mctx, prompt, true, true, ctx_server.init_opt);
4379        }
4380
4381        // tasks.reserve(inputs.size()); // TODO: this is inaccurate due to child tasks
4382
4383        // message delimiters for checkpointing
4384        json delims = json_value(data, "message_delimiters", json::array());
4385        auto delimiters = common_chat_msg_delimiters_parse(delims);
4386        delimiters.tokenize(ctx_server.vocab);
4387
4388        for (size_t i = 0; i < inputs.size(); i++) {
4389            server_task task = server_task(type);
4390
4391            task.id = rd.get_new_id();
4392
4393            task.tokens = std::move(inputs[i]);
4394            task.params = server_schema::eval_llama_cmpl_schema(
4395                    ctx_server.vocab,
4396                    params,
4397                    meta->logit_bias_eog,
4398                    data);
4399
4400            task.params.message_spans = task.tokens.find_message_spans(delimiters);
4401
4402            task.id_slot = json_value(data, "id_slot", -1);
4403            sse_ping_interval = task.params.sse_ping_interval;
4404
4405            // OAI-compat
4406            task.params.res_type          = res_type;
4407            task.params.oaicompat_cmpl_id = completion_id;
4408            task.params.oaicompat_model   = meta->model_name;
4409
4410            // prepare child tasks
4411            if (task.params.n_cmpl > 1) {
4412                int n_children = task.params.n_cmpl - 1;
4413                for (int j = 0; j < n_children; j++) {
4414                    task.add_child(task.id, rd.get_new_id());
4415                }
4416            }
4417
4418            tasks.push_back(std::move(task));
4419        }
4420
4421        rd.post_tasks(std::move(tasks));
4422    } catch (const std::exception & e) {
4423        res->error(format_error_response(e.what(), ERROR_TYPE_INVALID_REQUEST));
4424        return res;
4425    }
4426
4427    bool stream = json_value(data, "stream", false);
4428
4429    if (!stream) {
4430        // non-stream, wait for the results
4431        auto all_results = rd.wait_for_all(req.should_stop);
4432        if (all_results.is_terminated) {
4433            return res; // connection is closed
4434        } else if (all_results.error) {
4435            res->error(all_results.error->to_json());
4436            return res;
4437        } else {
4438            json arr = json::array();
4439            for (auto & res : all_results.results) {
4440                GGML_ASSERT(dynamic_cast<server_task_result_cmpl_final*>(res.get()) != nullptr);
4441                arr.push_back(res->to_json());
4442            }
4443            GGML_ASSERT(!arr.empty() && "empty results");
4444            if (arr.size() == 1) {
4445                // if single request, return single object instead of array
4446                res->ok(arr[0]);
4447            } else if (res_type == TASK_RESPONSE_TYPE_OAI_CHAT || res_type == TASK_RESPONSE_TYPE_OAI_CMPL) {
4448                // if multiple results in OAI format, we need to re-format them
4449                json & choices = arr[0]["choices"];
4450                for (size_t i = 1; i < arr.size(); i++) {
4451                    choices.push_back(std::move(arr[i]["choices"][0]));
4452                }
4453                res->ok(arr[0]);
4454            } else {
4455                // multi-results, non-OAI compat
4456                res->ok(arr);
4457            }
4458        }
4459    } else {
4460        // in streaming mode, the first error must be treated as non-stream response
4461        // this is to match the OAI API behavior
4462        // ref: https://github.com/ggml-org/llama.cpp/pull/16486#discussion_r2419657309
4463        auto first_result = rd.next(req.should_stop);
4464        if (first_result == nullptr) {
4465            GGML_ASSERT(req.should_stop());
4466            return res; // connection is closed
4467        }
4468
4469        if (first_result->is_error()) {
4470            res->error(first_result->to_json());
4471            return res;
4472        }
4473
4474        GGML_ASSERT(
4475            dynamic_cast<server_task_result_cmpl_partial*>(first_result.get()) != nullptr ||
4476            dynamic_cast<server_task_result_cmpl_final*>  (first_result.get()) != nullptr
4477        );
4478
4479        // next responses are streamed
4480        // to be sent immediately
4481        json first_result_json = first_result->to_json();
4482        if (first_result_json == nullptr) {
4483            res->data = ""; // simply send HTTP headers and status code
4484        } else if (res_type == TASK_RESPONSE_TYPE_ANTHROPIC) {
4485            res->data = format_anthropic_sse(first_result_json);
4486        } else if (res_type == TASK_RESPONSE_TYPE_OAI_RESP) {
4487            res->data = format_oai_resp_sse(first_result_json);
4488        } else {
4489            res->data = format_oai_sse(first_result_json);
4490        }
4491        res->status = 200;
4492        res->content_type = "text/event-stream";
4493        res->set_next([res_this = res.get(), res_type, sse_ping_interval](std::string & output) -> bool {
4494            static auto format_error = [](task_response_type res_type, const json & res_json) {
4495                if (res_type == TASK_RESPONSE_TYPE_ANTHROPIC) {
4496                    return format_anthropic_sse({
4497                        {"event", "error"},
4498                        {"data", res_json},
4499                    });
4500                } else {
4501                    return format_oai_sse(json {{ "error", res_json }});
4502                }
4503            };
4504
4505            auto effective_should_stop = [&res_this]() {
4506                return res_this->should_stop();
4507            };
4508
4509            try {
4510                if (effective_should_stop()) {
4511                    SRV_DBG("%s", "stopping streaming due to should_stop condition\n");
4512                    return false; // should_stop condition met
4513                }
4514
4515                if (!res_this->data.empty()) {
4516                    // flush the first chunk
4517                    output = std::move(res_this->data);
4518                    res_this->data.clear();
4519                    return true;
4520                }
4521
4522                server_response_reader & rd = res_this->rd;
4523
4524                // check if there is more data
4525                if (!rd.has_next()) {
4526                    switch (res_type) {
4527                        case TASK_RESPONSE_TYPE_NONE:
4528                        case TASK_RESPONSE_TYPE_OAI_RESP:
4529                        case TASK_RESPONSE_TYPE_ANTHROPIC:
4530                            output = "";
4531                            break;
4532
4533                        default:
4534                            output = "data: [DONE]\n\n";
4535                            break;
4536                    }
4537                    SRV_DBG("%s", "all results received, terminating stream\n");
4538                    return false; // no more data, terminate
4539                }
4540
4541                // receive subsequent results
4542                bool timeout = false;
4543                int64_t start_time = ggml_time_ms();
4544                auto result = rd.next([&timeout, &start_time, sse_ping_interval, &effective_should_stop]() {
4545                    if (effective_should_stop()) {
4546                        return true; // should_stop condition met
4547                    } else if (sse_ping_interval > 0 && ggml_time_ms() - start_time > (int64_t)sse_ping_interval * 1000) {
4548                        timeout = true;
4549                        return true; // timeout
4550                    }
4551                    return false;
4552                });
4553
4554                if (timeout) {
4555                    // some clients may time out (e.g. undici) will time out if no data is received for a while, so we need to send a ping to keep the connection alive
4556                    SRV_DBG("%s", "sending SSE ping\n");
4557                    output = ":\n\n";
4558                    return true;
4559                }
4560
4561                if (result == nullptr) {
4562                    SRV_DBG("%s", "stopping streaming due to should_stop condition\n");
4563                    GGML_ASSERT(effective_should_stop());
4564                    return false; // should_stop condition met
4565                }
4566
4567                // send the results
4568                if (result->is_error()) {
4569                    json res_json = result->to_json();
4570                    output = format_error(res_type, res_json);
4571                    SRV_DBG("%s", "error received during streaming, terminating stream\n");
4572                    return false; // terminate on error
4573                } else {
4574                    GGML_ASSERT(
4575                        dynamic_cast<server_task_result_cmpl_partial*>(result.get()) != nullptr
4576                        || dynamic_cast<server_task_result_cmpl_final*>(result.get()) != nullptr
4577                    );
4578                    json res_json = result->to_json();
4579                    if (res_type == TASK_RESPONSE_TYPE_ANTHROPIC) {
4580                        output = format_anthropic_sse(res_json);
4581                    } else if (res_type == TASK_RESPONSE_TYPE_OAI_RESP) {
4582                        output = format_oai_resp_sse(res_json);
4583                    } else {
4584                        output = format_oai_sse(res_json);
4585                    }
4586                }
4587
4588                // has next data, continue
4589                return true;
4590
4591            } catch (const std::exception & e) {
4592                json error_json = format_error_response(e.what(), ERROR_TYPE_SERVER);
4593                output = format_error(res_type, error_json);
4594
4595                // terminate on exception
4596                return false;
4597            }
4598        });
4599    }
4600
4601    return res;
4602}
4603
4604std::unique_ptr<server_res_generator> server_routes::create_response(bool bypass_sleep) {
4605    return std::make_unique<server_res_generator>(queue_tasks, queue_results, params.sleep_idle_seconds, bypass_sleep);
4606}
4607
4608server_routes::server_routes(const common_params & params, server_context & ctx_server)
4609        : params(params),
4610          ctx_server(*ctx_server.impl),
4611          queue_tasks(ctx_server.impl->queue_tasks),
4612          queue_results(ctx_server.impl->queue_results) {
4613    init_routes();
4614
4615    // note: this must be registered before load_model()
4616    //       so that on sleep phase, the callback is called before ctx is destroyed
4617    queue_tasks.on_sleeping_state([this](bool is_sleeping) {
4618        update_cached_responses(is_sleeping);
4619    });
4620}
4621
4622static json get_res_model_info(const server_context_meta & meta) {
4623    // note: do NOT use ctx_server here, otherwise it's not possible to use this during sleep
4624
4625    return {
4626        {"id",       meta.model_name},
4627        {"aliases",  meta.model_aliases},
4628        {"tags",     meta.model_tags},
4629        {"object",   "model"},
4630        {"created",  std::time(0)},
4631        {"owned_by", "llamacpp"},
4632        {"meta",     {
4633            {"vocab_type",  meta.model_vocab_type},
4634            {"n_vocab",     meta.model_vocab_n_tokens},
4635            {"n_ctx",       meta.slot_n_ctx},
4636            {"n_ctx_train", meta.model_n_ctx_train},
4637            {"n_embd",      meta.model_n_embd_inp},
4638            {"n_params",    meta.model_n_params},
4639            {"size",        meta.model_size},
4640            {"ftype",       meta.model_ftype},
4641        }},
4642    };
4643}
4644
4645static json get_res_models(const server_context_meta & meta) {
4646    // note: do NOT use ctx_server here, otherwise it's not possible to use this during sleep
4647
4648    return json{
4649        {"models", json::array({
4650            {
4651                {"name",  meta.model_name},
4652                {"model", meta.model_name},
4653                {"modified_at", ""},
4654                {"size", ""},
4655                {"digest", ""}, // dummy value, llama.cpp does not support managing model file's hash
4656                {"type", "model"},
4657                {"description", ""},
4658                {"tags", json::array({""})},
4659                {"capabilities", meta.has_mtmd ? json::array({"completion","multimodal"}) : json::array({"completion"})},
4660                {"parameters", ""},
4661                {"details", {
4662                    {"parent_model", ""},
4663                    {"format", "gguf"},
4664                    {"family", ""},
4665                    {"families", json::array({""})},
4666                    {"parameter_size", ""},
4667                    {"quantization_level", ""}
4668                }}
4669            }
4670        })},
4671        {"object", "list"},
4672        {"data", json::array({
4673            get_res_model_info(meta),
4674        })}
4675    };
4676}
4677
4678static json get_res_props(const server_context_meta & meta, const common_params & params, bool is_sleeping) {
4679    // note: do NOT use ctx_server here, otherwise it's not possible to use this during sleep
4680
4681    task_params tparams;
4682    tparams.sampling = params.sampling;
4683    json default_generation_settings_for_props = json {
4684        { "params", tparams.to_json(true) },
4685        { "n_ctx",  meta.slot_n_ctx },
4686    };
4687
4688    std::string tmpl_default = common_chat_templates_source(meta.chat_params.tmpls.get(), "");
4689    std::string tmpl_tools   = common_chat_templates_source(meta.chat_params.tmpls.get(), "tool_use");
4690
4691    json props = {
4692        { "default_generation_settings", default_generation_settings_for_props },
4693        { "total_slots",                 params.n_parallel },
4694        { "model_alias",                 meta.model_name },
4695        { "model_ftype",                 meta.model_ftype },
4696        { "model_path",                  meta.model_path },
4697        { "modalities",                  json {
4698            {"vision", meta.has_inp_image},
4699            {"video",  meta.has_inp_video},
4700            {"audio",  meta.has_inp_audio},
4701        } },
4702        { "media_marker",                get_media_marker() },
4703        { "endpoint_slots",              params.endpoint_slots },
4704        { "endpoint_props",              params.endpoint_props },
4705        { "endpoint_metrics",            params.endpoint_metrics },
4706        { "ui",                          params.ui },
4707        { "ui_settings",                 meta.json_ui_settings },
4708        { "chat_template",               tmpl_default },
4709        { "chat_template_caps",          meta.chat_template_caps },
4710        { "bos_token",                   meta.bos_token_str },
4711        { "eos_token",                   meta.eos_token_str },
4712        { "build_info",                  meta.build_info },
4713        { "is_sleeping",                 is_sleeping },
4714        { "cors_proxy_enabled",          params.ui_mcp_proxy },
4715    };
4716    if (params.use_jinja) {
4717        if (!tmpl_tools.empty()) {
4718            props["chat_template_tool_use"] = tmpl_tools;
4719        }
4720    }
4721
4722    return props;
4723}
4724
4725json server_routes::get_model_info() const {
4726    return get_res_model_info(*meta);
4727}
4728
4729void server_routes::init_routes() {
4730    // IMPORTANT: all lambda functions must start with create_response()
4731    // this is to ensure that the server_res_generator can handle sleeping case correctly
4732
4733    this->get_health = [this](const server_http_req &) {
4734        // error and loading states are handled by middleware
4735        auto res = create_response(true);
4736
4737        // this endpoint can be accessed during sleeping
4738        // the next LOC is to avoid someone accidentally use ctx_server
4739        bool ctx_server; // do NOT delete this line
4740        GGML_UNUSED(ctx_server);
4741
4742        res->ok({{"status", "ok"}});
4743        return res;
4744    };
4745
4746    this->get_metrics = [this](const server_http_req & req) {
4747        auto res = create_response(true);
4748        if (!params.endpoint_metrics) {
4749            res->error(format_error_response("This server does not support metrics endpoint. Start it with `--metrics`", ERROR_TYPE_NOT_SUPPORTED));
4750            return res;
4751        }
4752
4753        // render response using cached_metrics
4754        auto use_cached_metrics = [&]() {
4755            std::unique_lock<std::mutex> lock(mutex_cache);
4756            res->headers["Process-Start-Time-Unix"] = std::to_string(cached_metrics.t_start);
4757            server_task_result_metrics tmp;
4758            tmp.metrics = cached_metrics;
4759            res->content_type = "text/plain; version=0.0.4";
4760            res->status = 200;
4761            res->data = tmp.to_metrics();
4762            // the gauges are averaged over the window between two scrapes
4763            cached_metrics.reset_bucket();
4764            should_reset_buckets = true;
4765        };
4766
4767        if (queue_tasks.is_sleeping()) {
4768            use_cached_metrics();
4769
4770        } else {
4771            // request slots data using task queue
4772            {
4773                server_task task(SERVER_TASK_TYPE_METRICS);
4774                task.id = res->rd.get_new_id();
4775                // the gauges are averaged over the window between two scrapes
4776                task.metrics_reset_bucket = true;
4777                res->rd.post_task(std::move(task), true); // high-priority task
4778            }
4779
4780            // a task posted right before sleeping is never processed, do not wait for it
4781            auto result = res->rd.next([&]{
4782                return req.should_stop() || queue_tasks.is_sleeping();
4783            });
4784            if (!result) {
4785                if (!req.should_stop()) {
4786                    use_cached_metrics();
4787                }
4788                return res;
4789            }
4790
4791            if (result->is_error()) {
4792                res->error(result->to_json());
4793                return res;
4794            }
4795
4796            auto res_task = dynamic_cast<server_task_result_metrics*>(result.get());
4797            GGML_ASSERT(res_task != nullptr);
4798
4799            res->headers["Process-Start-Time-Unix"] = std::to_string(res_task->metrics.t_start);
4800            res->content_type = "text/plain; version=0.0.4";
4801            res->status = 200;
4802            res->data = res_task->to_metrics();
4803        }
4804
4805        return res;
4806    };
4807
4808    this->get_slots = [this](const server_http_req & req) {
4809        auto res = create_response();
4810        if (!params.endpoint_slots) {
4811            res->error(format_error_response("This server does not support slots endpoint. Start it with `--slots`", ERROR_TYPE_NOT_SUPPORTED));
4812            return res;
4813        }
4814
4815        // request slots data using task queue
4816        {
4817            server_task task(SERVER_TASK_TYPE_SLOT_GET);
4818            task.id = res->rd.get_new_id();
4819            res->rd.post_task(std::move(task), true); // high-priority task
4820        }
4821
4822        // get the result
4823        auto result = res->rd.next(req.should_stop);
4824        if (!result) {
4825            // connection was closed
4826            GGML_ASSERT(req.should_stop());
4827            return res;
4828        }
4829
4830        if (result->is_error()) {
4831            res->error(result->to_json());
4832            return res;
4833        }
4834
4835        auto * res_task = dynamic_cast<server_task_result_slots*>(result.get());
4836        GGML_ASSERT(res_task != nullptr);
4837
4838        // optionally return "fail_on_no_slot" error
4839        if (!req.get_param("fail_on_no_slot").empty()) {
4840            if (res_task->n_idle_slots == 0) {
4841                res->error(format_error_response("no slot available", ERROR_TYPE_UNAVAILABLE));
4842                return res;
4843            }
4844        }
4845
4846        res->ok(res_task->to_json());
4847        return res;
4848    };
4849
4850    this->post_slots = [this](const server_http_req & req) {
4851        auto res = create_response();
4852        if (params.slot_save_path.empty()) {
4853            res->error(format_error_response("This server does not support slots action. Start it with `--slot-save-path`", ERROR_TYPE_NOT_SUPPORTED));
4854            return res;
4855        }
4856
4857        std::string id_slot_str = req.get_param("id_slot");
4858
4859        int id_slot;
4860        try {
4861            id_slot = std::stoi(id_slot_str);
4862        } catch (const std::exception &) {
4863            res->error(format_error_response("Invalid slot ID", ERROR_TYPE_INVALID_REQUEST));
4864            return res;
4865        }
4866
4867        std::string action = req.get_param("action");
4868
4869        if (action == "save") {
4870            return handle_slots_save(req, id_slot);
4871        }
4872        if (action == "restore") {
4873            return handle_slots_restore(req, id_slot);
4874        }
4875        if (action == "erase") {
4876            return handle_slots_erase(req, id_slot);
4877        }
4878
4879        res->error(format_error_response("Invalid action", ERROR_TYPE_INVALID_REQUEST));
4880        return res;
4881    };
4882
4883    this->get_props = [this](const server_http_req &) {
4884        auto res = create_response(true);
4885        // note: do NOT use ctx_server here, this endpoint must be accessible during sleep
4886        if (queue_tasks.is_sleeping()) {
4887            std::unique_lock<std::mutex> lock(mutex_cache);
4888            res->ok(cached_props);
4889        } else {
4890            res->ok(get_res_props(*meta, params, false));
4891        }
4892        return res;
4893    };
4894
4895    this->post_props = [this](const server_http_req &) {
4896        auto res = create_response();
4897        if (!params.endpoint_props) {
4898            res->error(format_error_response("This server does not support changing global properties. Start it with `--props`", ERROR_TYPE_NOT_SUPPORTED));
4899            return res;
4900        }
4901        // update any props here
4902
4903        res->ok({{ "success", true }});
4904        return res;
4905    };
4906
4907    this->post_infill = [this](const server_http_req & req) {
4908        auto res = create_response();
4909        // check model compatibility
4910        std::string err;
4911        if (llama_vocab_fim_pre(ctx_server.vocab) == LLAMA_TOKEN_NULL) {
4912            err += "prefix token is missing. ";
4913        }
4914        if (llama_vocab_fim_suf(ctx_server.vocab) == LLAMA_TOKEN_NULL) {
4915            err += "suffix token is missing. ";
4916        }
4917        if (llama_vocab_fim_mid(ctx_server.vocab) == LLAMA_TOKEN_NULL) {
4918            err += "middle token is missing. ";
4919        }
4920        if (!err.empty()) {
4921            res->error(format_error_response(string_format("Infill is not supported by this model: %s", err.c_str()), ERROR_TYPE_NOT_SUPPORTED));
4922            return res;
4923        }
4924
4925        // validate input
4926        json data = json::parse(req.body);
4927        if (data.contains("prompt") && !data.at("prompt").is_string()) {
4928            // prompt is optional
4929            res->error(format_error_response("\"prompt\" must be a string", ERROR_TYPE_INVALID_REQUEST));
4930        }
4931
4932        if (!data.contains("input_prefix")) {
4933            res->error(format_error_response("\"input_prefix\" is required", ERROR_TYPE_INVALID_REQUEST));
4934        }
4935
4936        if (!data.contains("input_suffix")) {
4937            res->error(format_error_response("\"input_suffix\" is required", ERROR_TYPE_INVALID_REQUEST));
4938        }
4939
4940        if (data.contains("input_extra") && !data.at("input_extra").is_array()) {
4941            // input_extra is optional
4942            res->error(format_error_response("\"input_extra\" must be an array of {\"filename\": string, \"text\": string}", ERROR_TYPE_INVALID_REQUEST));
4943            return res;
4944        }
4945
4946        json input_extra = json_value(data, "input_extra", json::array());
4947        for (const auto & chunk : input_extra) {
4948            // { "text": string, "filename": string }
4949            if (!chunk.contains("text") || !chunk.at("text").is_string()) {
4950                res->error(format_error_response("extra_context chunk must contain a \"text\" field with a string value", ERROR_TYPE_INVALID_REQUEST));
4951                return res;
4952            }
4953            // filename is optional
4954            if (chunk.contains("filename") && !chunk.at("filename").is_string()) {
4955                res->error(format_error_response("extra_context chunk's \"filename\" field must be a string", ERROR_TYPE_INVALID_REQUEST));
4956                return res;
4957            }
4958        }
4959        data["input_extra"] = input_extra; // default to empty array if it's not exist
4960
4961        std::string prompt = json_value(data, "prompt", std::string());
4962        std::vector<server_tokens> tokenized_prompts = tokenize_input_prompts(ctx_server.vocab, ctx_server.mctx, prompt, false, true, ctx_server.init_opt);
4963        SRV_DBG("creating infill tasks, n_prompts = %d\n", (int) tokenized_prompts.size());
4964        data["prompt"] = format_prompt_infill(
4965            ctx_server.vocab,
4966            data.at("input_prefix"),
4967            data.at("input_suffix"),
4968            data.at("input_extra"),
4969            params.n_batch,
4970            params.n_predict,
4971            meta->slot_n_ctx,
4972            params.spm_infill,
4973            tokenized_prompts[0].get_tokens() // TODO: this could maybe be multimodal.
4974        );
4975
4976        std::vector<raw_buffer> files; // dummy
4977        return handle_completions_impl(
4978            req,
4979            SERVER_TASK_TYPE_INFILL,
4980            data,
4981            files,
4982            TASK_RESPONSE_TYPE_NONE); // infill is not OAI compatible
4983    };
4984
4985    this->post_completions = [this](const server_http_req & req) {
4986        auto res = create_response();
4987        std::vector<raw_buffer> files; // dummy
4988        const json body = json::parse(req.body);
4989        return handle_completions_impl(
4990            req,
4991            SERVER_TASK_TYPE_COMPLETION,
4992            body,
4993            files,
4994            TASK_RESPONSE_TYPE_NONE);
4995    };
4996
4997    this->post_completions_oai = [this](const server_http_req & req) {
4998        auto res = create_response();
4999        std::vector<raw_buffer> files; // dummy
5000        const json body = json::parse(req.body);
5001        return handle_completions_impl(
5002            req,
5003            SERVER_TASK_TYPE_COMPLETION,
5004            body,
5005            files,
5006            TASK_RESPONSE_TYPE_OAI_CMPL);
5007    };
5008
5009    this->post_chat_completions = [this](const server_http_req & req) {
5010        auto res = create_response();
5011        std::vector<raw_buffer> files;
5012        json body = json::parse(req.body);
5013        json body_parsed = oaicompat_chat_params_parse(
5014            body,
5015            meta->chat_params,
5016            files);
5017        return handle_completions_impl(
5018            req,
5019            SERVER_TASK_TYPE_COMPLETION,
5020            body_parsed,
5021            files,
5022            TASK_RESPONSE_TYPE_OAI_CHAT);
5023    };
5024
5025    this->post_chat_completions_tok = [this](const server_http_req & req) {
5026        return handle_count_tokens(ctx_server.vocab, ctx_server.mctx, ctx_server.init_opt, req, TASK_RESPONSE_TYPE_OAI_CHAT);
5027    };
5028
5029    this->post_control = [this](const server_http_req & req) {
5030        auto res = create_response();
5031        const json body = json::parse(req.body);
5032
5033        const std::string cmpl_id = json_value(body, "id", std::string());
5034        const std::string action  = json_value(body, "action", std::string());
5035        if (cmpl_id.empty()) {
5036            res->error(format_error_response("missing completion id", ERROR_TYPE_INVALID_REQUEST));
5037            return res;
5038        }
5039        if (action != "reasoning_end") {
5040            res->error(format_error_response("unknown control action", ERROR_TYPE_INVALID_REQUEST));
5041            return res;
5042        }
5043
5044        auto & rd = res->rd;
5045        {
5046            server_task task(SERVER_TASK_TYPE_CONTROL);
5047            task.id              = rd.get_new_id();
5048            task.params.control_cmpl_id = cmpl_id;
5049            task.params.control_action  = action;
5050            rd.post_task(std::move(task));
5051        }
5052
5053        auto result = rd.next(req.should_stop);
5054        if (!result) {
5055            GGML_ASSERT(req.should_stop());
5056            return res;
5057        }
5058        if (result->is_error()) {
5059            res->error(result->to_json());
5060            return res;
5061        }
5062        res->ok(result->to_json());
5063        return res;
5064    };
5065
5066    this->post_responses_oai = [this](const server_http_req & req) {
5067        auto res = create_response();
5068        std::vector<raw_buffer> files;
5069        json body = server_chat_convert_responses_to_chatcmpl(json::parse(req.body));
5070        SRV_DBG("%s\n", "Request converted: OpenAI Responses -> OpenAI Chat Completions");
5071        SRV_DBG("converted request: %s\n", body.dump().c_str());
5072        json body_parsed = oaicompat_chat_params_parse(
5073            body,
5074            meta->chat_params,
5075            files);
5076        return handle_completions_impl(
5077            req,
5078            SERVER_TASK_TYPE_COMPLETION,
5079            body_parsed,
5080            files,
5081            TASK_RESPONSE_TYPE_OAI_RESP);
5082    };
5083
5084    this->post_responses_tok_oai = [this](const server_http_req & req) {
5085        return handle_count_tokens(ctx_server.vocab, ctx_server.mctx, ctx_server.init_opt, req, TASK_RESPONSE_TYPE_OAI_RESP);
5086    };
5087
5088    this->post_transcriptions_oai = [this](const server_http_req & req) {
5089        auto res = create_response();
5090
5091        if (!meta->has_mtmd || !meta->chat_params.allow_audio) {
5092            res->error(format_error_response("The current model does not support audio input.", ERROR_TYPE_NOT_SUPPORTED));
5093            return res;
5094        }
5095
5096        std::vector<raw_buffer> files;
5097        json body = convert_transcriptions_to_chatcmpl(
5098            json::parse(req.body),
5099            meta->chat_params.tmpls.get(),
5100            req.files,
5101            files);
5102        SRV_DBG("%s\n", "Request converted: OpenAI Transcriptions -> OpenAI Chat Completions");
5103        SRV_DBG("converted request: %s\n", body.dump().c_str());
5104        json body_parsed = oaicompat_chat_params_parse(
5105            body,
5106            meta->chat_params,
5107            files);
5108        return handle_completions_impl(
5109            req,
5110            SERVER_TASK_TYPE_COMPLETION,
5111            body_parsed,
5112            files,
5113            TASK_RESPONSE_TYPE_OAI_ASR);
5114    };
5115
5116    this->post_anthropic_messages = [this](const server_http_req & req) {
5117        auto res = create_response();
5118        std::vector<raw_buffer> files;
5119        json body = server_chat_convert_anthropic_to_oai(json::parse(req.body));
5120        SRV_DBG("%s\n", "Request converted: Anthropic -> OpenAI Chat Completions");
5121        SRV_DBG("converted request: %s\n", body.dump().c_str());
5122        json body_parsed = oaicompat_chat_params_parse(
5123            body,
5124            meta->chat_params,
5125            files);
5126        return handle_completions_impl(
5127            req,
5128            SERVER_TASK_TYPE_COMPLETION,
5129            body_parsed,
5130            files,
5131            TASK_RESPONSE_TYPE_ANTHROPIC);
5132    };
5133
5134    this->post_anthropic_count_tokens = [this](const server_http_req & req) {
5135        return handle_count_tokens(ctx_server.vocab, ctx_server.mctx, ctx_server.init_opt, req, TASK_RESPONSE_TYPE_ANTHROPIC);
5136    };
5137
5138    // same with handle_chat_completions, but without inference part
5139    this->post_apply_template = [this](const server_http_req & req) {
5140        auto res = create_response();
5141        std::vector<raw_buffer> files; // dummy, unused
5142        json body = json::parse(req.body);
5143        json data = oaicompat_chat_params_parse(
5144            body,
5145            meta->chat_params,
5146            files);
5147        res->ok({{ "prompt", std::move(data.at("prompt")) }});
5148        return res;
5149    };
5150
5151    this->get_models = [this](const server_http_req &) {
5152        auto res = create_response(true);
5153        // note: do NOT use ctx_server here, this endpoint must be accessible during sleep
5154        if (queue_tasks.is_sleeping()) {
5155            std::unique_lock<std::mutex> lock(mutex_cache);
5156            res->ok(cached_models);
5157        } else {
5158            res->ok(get_res_models(*meta));
5159        }
5160        return res;
5161    };
5162
5163    this->post_tokenize = [this](const server_http_req & req) {
5164        auto res = create_response();
5165        const json body = json::parse(req.body);
5166        json tokens_response = json::array();
5167        if (body.count("content") != 0) {
5168            const bool add_special = json_value(body, "add_special", false);
5169            const bool parse_special = json_value(body, "parse_special", true);
5170            const bool with_pieces = json_value(body, "with_pieces", false);
5171
5172            llama_tokens tokens = tokenize_mixed(ctx_server.vocab, body.at("content"), add_special, parse_special);
5173
5174            if (with_pieces) {
5175                for (const auto& token : tokens) {
5176                    std::string piece = common_token_to_piece(ctx_server.vocab, token);
5177                    json piece_json;
5178
5179                    // Check if the piece is valid UTF-8
5180                    if (is_valid_utf8(piece)) {
5181                        piece_json = piece;
5182                    } else {
5183                        // If not valid UTF-8, store as array of byte values
5184                        piece_json = json::array();
5185                        for (unsigned char c : piece) {
5186                            piece_json.push_back(static_cast<int>(c));
5187                        }
5188                    }
5189
5190                    tokens_response.push_back({
5191                        {"id", token},
5192                        {"piece", piece_json}
5193                    });
5194                }
5195            } else {
5196                tokens_response = tokens;
5197            }
5198        }
5199
5200        res->ok(json{{"tokens", std::move(tokens_response)}});
5201        return res;
5202    };
5203
5204    this->post_detokenize = [this](const server_http_req & req) {
5205        auto res = create_response();
5206        const json body = json::parse(req.body);
5207
5208        std::string content;
5209        if (body.count("tokens") != 0) {
5210            const llama_tokens tokens = body.at("tokens").get<llama_tokens>();
5211            content = tokens_to_str(ctx_server.vocab, tokens);
5212        }
5213
5214        res->ok(json{{"content", std::move(content)}});
5215        return res;
5216    };
5217
5218    this->post_embeddings = [this](const server_http_req & req) {
5219        return handle_embeddings_impl(req, TASK_RESPONSE_TYPE_NONE);
5220    };
5221
5222    this->post_embeddings_oai = [this](const server_http_req & req) {
5223        return handle_embeddings_impl(req, TASK_RESPONSE_TYPE_OAI_EMBD);
5224    };
5225
5226    this->post_rerank = [this](const server_http_req & req) {
5227        auto res = create_response();
5228        if (!params.embedding || params.pooling_type != LLAMA_POOLING_TYPE_RANK) {
5229            res->error(format_error_response("This server does not support reranking. Start it with `--reranking`", ERROR_TYPE_NOT_SUPPORTED));
5230            return res;
5231        }
5232
5233        const json body = json::parse(req.body);
5234
5235        // if true, use TEI API format, otherwise use Jina API format
5236        // Jina: https://jina.ai/reranker/
5237        // TEI: https://huggingface.github.io/text-embeddings-inference/#/Text%20Embeddings%20Inference/rerank
5238        bool is_tei_format = body.contains("texts");
5239
5240        json query;
5241        if (body.count("query") == 1) {
5242            query = body.at("query");
5243            if (!query.is_string()) {
5244                res->error(format_error_response("\"query\" must be a string", ERROR_TYPE_INVALID_REQUEST));
5245                return res;
5246            }
5247        } else {
5248            res->error(format_error_response("\"query\" must be provided", ERROR_TYPE_INVALID_REQUEST));
5249            return res;
5250        }
5251
5252        std::vector<std::string> documents = json_value(body, "documents",
5253                                             json_value(body, "texts", std::vector<std::string>()));
5254        if (documents.empty()) {
5255            res->error(format_error_response("\"documents\" must be a non-empty string array", ERROR_TYPE_INVALID_REQUEST));
5256            return res;
5257        }
5258
5259        int top_n = json_value(body, "top_n", (int)documents.size());
5260
5261        // create and queue the task
5262        json responses = json::array();
5263        auto & rd = res->rd;
5264        {
5265            std::vector<server_task> tasks;
5266            tasks.reserve(documents.size());
5267            for (size_t i = 0; i < documents.size(); i++) {
5268                auto tmp = format_prompt_rerank(ctx_server.model_tgt, ctx_server.vocab, ctx_server.mctx, query, documents[i], ctx_server.init_opt);
5269                server_task task = server_task(SERVER_TASK_TYPE_RERANK);
5270                task.id     = rd.get_new_id();
5271                task.tokens = std::move(tmp);
5272                tasks.push_back(std::move(task));
5273            }
5274            rd.post_tasks(std::move(tasks));
5275        }
5276
5277        // wait for the results
5278        auto all_results = rd.wait_for_all(req.should_stop);
5279
5280        // collect results
5281        if (all_results.is_terminated) {
5282            return res; // connection is closed
5283        } else if (all_results.error) {
5284            res->error(all_results.error->to_json());
5285            return res;
5286        } else {
5287            for (auto & res : all_results.results) {
5288                GGML_ASSERT(dynamic_cast<server_task_result_rerank*>(res.get()) != nullptr);
5289                responses.push_back(res->to_json());
5290            }
5291        }
5292
5293        // write JSON response
5294        json root = format_response_rerank(
5295            body,
5296            meta->model_name,
5297            responses,
5298            is_tei_format,
5299            documents,
5300            top_n);
5301
5302        res->ok(root);
5303        return res;
5304    };
5305
5306    this->get_lora_adapters = [this](const server_http_req & req) {
5307        auto res = create_response();
5308
5309        auto & rd = res->rd;
5310        {
5311            server_task task(SERVER_TASK_TYPE_GET_LORA);
5312            task.id = rd.get_new_id();
5313            rd.post_task(std::move(task));
5314        }
5315
5316        // get the result
5317        auto result = rd.next(req.should_stop);
5318        if (!result) {
5319            // connection was closed
5320            GGML_ASSERT(req.should_stop());
5321            return res;
5322        }
5323
5324        if (result->is_error()) {
5325            res->error(result->to_json());
5326            return res;
5327        }
5328
5329        GGML_ASSERT(dynamic_cast<server_task_result_get_lora*>(result.get()) != nullptr);
5330        res->ok(result->to_json());
5331        return res;
5332    };
5333
5334    this->post_lora_adapters = [this](const server_http_req & req) {
5335        auto res = create_response();
5336        const json body = json::parse(req.body);
5337        if (!body.is_array()) {
5338            res->error(format_error_response("Request body must be an array", ERROR_TYPE_INVALID_REQUEST));
5339            return res;
5340        }
5341
5342        auto & rd = res->rd;
5343        {
5344            server_task task(SERVER_TASK_TYPE_SET_LORA);
5345            task.id = rd.get_new_id();
5346            task.set_lora = parse_lora_request(body);
5347            rd.post_task(std::move(task));
5348        }
5349
5350        // get the result
5351        auto result = rd.next(req.should_stop);
5352        if (!result) {
5353            // connection was closed
5354            GGML_ASSERT(req.should_stop());
5355            return res;
5356        }
5357
5358        if (result->is_error()) {
5359            res->error(result->to_json());
5360            return res;
5361        }
5362
5363        GGML_ASSERT(dynamic_cast<server_task_result_apply_lora*>(result.get()) != nullptr);
5364        res->ok(result->to_json());
5365        return res;
5366    };
5367}
5368
5369std::unique_ptr<server_res_generator> server_routes::handle_slots_save(const server_http_req & req, int id_slot) {
5370    auto res = create_response();
5371    const json request_data = json::parse(req.body);
5372    std::string filename = request_data.at("filename");
5373    if (!fs_validate_filename(filename)) {
5374        res->error(format_error_response("Invalid filename", ERROR_TYPE_INVALID_REQUEST));
5375        return res;
5376    }
5377    std::string filepath = params.slot_save_path + filename;
5378
5379    auto & rd = res->rd;
5380    {
5381        server_task task(SERVER_TASK_TYPE_SLOT_SAVE);
5382        task.id = rd.get_new_id();
5383        task.slot_action.id_slot  = id_slot;
5384        task.slot_action.filename = filename;
5385        task.slot_action.filepath = filepath;
5386        rd.post_task(std::move(task));
5387    }
5388
5389    auto result = rd.next(req.should_stop);
5390    if (!result) {
5391        // connection was closed
5392        GGML_ASSERT(req.should_stop());
5393        return res;
5394    }
5395
5396    if (result->is_error()) {
5397        res->error(result->to_json());
5398        return res;
5399    }
5400
5401    res->ok(result->to_json());
5402    return res;
5403}
5404
5405std::unique_ptr<server_res_generator> server_routes::handle_slots_restore(const server_http_req & req, int id_slot) {
5406    auto res = create_response();
5407    const json request_data = json::parse(req.body);
5408    std::string filename = request_data.at("filename");
5409    if (!fs_validate_filename(filename)) {
5410        res->error(format_error_response("Invalid filename", ERROR_TYPE_INVALID_REQUEST));
5411        return res;
5412    }
5413    std::string filepath = params.slot_save_path + filename;
5414
5415    auto & rd = res->rd;
5416    {
5417        server_task task(SERVER_TASK_TYPE_SLOT_RESTORE);
5418        task.id = rd.get_new_id();
5419        task.slot_action.id_slot  = id_slot;
5420        task.slot_action.filename = filename;
5421        task.slot_action.filepath = filepath;
5422        rd.post_task(std::move(task));
5423    }
5424
5425    auto result = rd.next(req.should_stop);
5426    if (!result) {
5427        // connection was closed
5428        GGML_ASSERT(req.should_stop());
5429        return res;
5430    }
5431
5432    if (result->is_error()) {
5433        res->error(result->to_json());
5434        return res;
5435    }
5436
5437    GGML_ASSERT(dynamic_cast<server_task_result_slot_save_load*>(result.get()) != nullptr);
5438    res->ok(result->to_json());
5439    return res;
5440}
5441
5442std::unique_ptr<server_res_generator> server_routes::handle_slots_erase(const server_http_req & req, int id_slot) {
5443    auto res = create_response();
5444    auto & rd = res->rd;
5445    {
5446        server_task task(SERVER_TASK_TYPE_SLOT_ERASE);
5447        task.id = rd.get_new_id();
5448        task.slot_action.id_slot = id_slot;
5449        rd.post_task(std::move(task));
5450    }
5451
5452    auto result = rd.next(req.should_stop);
5453    if (!result) {
5454        // connection was closed
5455        GGML_ASSERT(req.should_stop());
5456        return res;
5457    }
5458
5459    if (result->is_error()) {
5460        res->error(result->to_json());
5461        return res;
5462    }
5463
5464    GGML_ASSERT(dynamic_cast<server_task_result_slot_erase*>(result.get()) != nullptr);
5465    res->ok(result->to_json());
5466    return res;
5467}
5468
5469std::unique_ptr<server_res_generator> server_routes::handle_embeddings_impl(const server_http_req & req, task_response_type res_type) {
5470    auto res = create_response();
5471    if (!params.embedding) {
5472        res->error(format_error_response("This server does not support embeddings. Start it with `--embeddings`", ERROR_TYPE_NOT_SUPPORTED));
5473        return res;
5474    }
5475
5476    if (res_type != TASK_RESPONSE_TYPE_NONE && meta->pooling_type == LLAMA_POOLING_TYPE_NONE) {
5477        res->error(format_error_response("Pooling type 'none' is not OAI compatible. Please use a different pooling type", ERROR_TYPE_INVALID_REQUEST));
5478        return res;
5479    }
5480
5481    const json body = json::parse(req.body);
5482
5483    // for the shape of input/content, see tokenize_input_prompts()
5484    json prompt;
5485    if (body.count("input") != 0) {
5486        prompt = body.at("input");
5487    } else if (body.contains("content")) {
5488        res_type = TASK_RESPONSE_TYPE_NONE; // "content" field is not OAI compatible
5489        prompt = body.at("content");
5490    } else {
5491        res->error(format_error_response("\"input\" or \"content\" must be provided", ERROR_TYPE_INVALID_REQUEST));
5492        return res;
5493    }
5494
5495    bool use_base64 = false;
5496    if (body.count("encoding_format") != 0) {
5497        const std::string & format = body.at("encoding_format");
5498        if (format == "base64") {
5499            use_base64 = true;
5500        } else if (format != "float") {
5501            res->error(format_error_response("The format to return the embeddings in. Can be either float or base64", ERROR_TYPE_INVALID_REQUEST));
5502            return res;
5503        }
5504    }
5505
5506    auto tokenized_prompts = tokenize_input_prompts(ctx_server.vocab, ctx_server.mctx, prompt, true, true, ctx_server.init_opt);
5507    for (const auto & tokens : tokenized_prompts) {
5508        // this check is necessary for models that do not add BOS token to the input
5509        if (tokens.empty()) {
5510            res->error(format_error_response("Input content cannot be empty", ERROR_TYPE_INVALID_REQUEST));
5511            return res;
5512        }
5513    }
5514
5515    int embd_normalize = params.embd_normalize;
5516    if (body.count("embd_normalize") != 0) {
5517        embd_normalize = body.at("embd_normalize").get<int>();
5518        if (meta->pooling_type == LLAMA_POOLING_TYPE_NONE) {
5519            SRV_DBG("embd_normalize is not supported by pooling type %d, ignoring it\n", meta->pooling_type);
5520        }
5521    }
5522
5523    // create and queue the task
5524    json responses = json::array();
5525    auto & rd = res->rd;
5526    {
5527        std::vector<server_task> tasks;
5528        for (size_t i = 0; i < tokenized_prompts.size(); i++) {
5529            server_task task = server_task(SERVER_TASK_TYPE_EMBEDDING);
5530
5531            task.id     = rd.get_new_id();
5532            task.tokens = std::move(tokenized_prompts[i]);
5533
5534            // OAI-compat
5535            task.params.res_type = res_type;
5536            task.params.embd_normalize = embd_normalize;
5537
5538            tasks.push_back(std::move(task));
5539        }
5540        rd.post_tasks(std::move(tasks));
5541    }
5542
5543    // wait for the results
5544    auto all_results = rd.wait_for_all(req.should_stop);
5545
5546    // collect results
5547    if (all_results.is_terminated) {
5548        return res; // connection is closed
5549    } else if (all_results.error) {
5550        res->error(all_results.error->to_json());
5551        return res;
5552    } else {
5553        for (auto & res : all_results.results) {
5554            GGML_ASSERT(dynamic_cast<server_task_result_embd*>(res.get()) != nullptr);
5555            responses.push_back(res->to_json());
5556        }
5557    }
5558
5559    // write JSON response
5560    json root = res_type == TASK_RESPONSE_TYPE_OAI_EMBD
5561        ? format_embeddings_response_oaicompat(body, meta->model_name, responses, use_base64)
5562        : json(responses);
5563    res->ok(root);
5564    return res;
5565}
5566
5567std::unique_ptr<server_res_generator> server_routes::handle_count_tokens(const llama_vocab * vocab, mtmd_context * mctx, const mtmd_helper_init_opt & init_opt, const server_http_req & req, task_response_type res_type) {
5568    auto res = create_response();
5569    std::vector<raw_buffer> files;
5570    json body = json::parse(req.body);
5571    bool is_oai = false;
5572
5573    switch (res_type) {
5574        case TASK_RESPONSE_TYPE_OAI_CHAT:
5575            {
5576                is_oai = true;
5577            } break;
5578        case TASK_RESPONSE_TYPE_OAI_RESP:
5579            {
5580                is_oai = true;
5581                body = server_chat_convert_responses_to_chatcmpl(body);
5582            } break;
5583        case TASK_RESPONSE_TYPE_ANTHROPIC:
5584            {
5585                body = server_chat_convert_anthropic_to_oai(body);
5586            } break;
5587        default:
5588            res->error(format_error_response("invalid res_type", ERROR_TYPE_INVALID_REQUEST));
5589            return res;
5590    }
5591
5592    json body_parsed = oaicompat_chat_params_parse(
5593            body,
5594            meta->chat_params,
5595            files);
5596    json prompt = body_parsed.at("prompt");
5597    // SRV_DBG("prompt = %s\n", prompt.dump().c_str());
5598
5599    // TODO @ngxson : refactor this code block, move this to server-common and reuse it in other places
5600    size_t n_tokens;
5601    if (mctx != nullptr) {
5602        if (!prompt.is_string()) {
5603            throw std::runtime_error("for mtmd, input prompt must be a string.");
5604        }
5605        n_tokens = process_mtmd_prompt(mctx, prompt.get<std::string>(), files, init_opt, true).size();
5606    } else {
5607        n_tokens = tokenize_mixed(vocab, prompt, true, true).size();
5608    }
5609
5610    json response = {{"input_tokens", static_cast<int64_t>(n_tokens)}};
5611    if (is_oai) {
5612        response["object"] = "response.input_tokens";
5613    }
5614    res->ok(response);
5615    return res;
5616}
5617
5618void server_routes::update_cached_responses(bool is_sleeping) {
5619    // caller is task_queue, so ctx_server can be accessed without holding locks
5620    std::unique_lock<std::mutex> lock(mutex_cache);
5621
5622    if (is_sleeping) {
5623        cached_models  = get_res_models(*meta);
5624        cached_props   = get_res_props(*meta, params, true);
5625        cached_metrics = ctx_server.get_metrics();
5626
5627        should_reset_buckets = false;
5628
5629        SRV_DBG("%s\n", "cached responses updated");
5630
5631    } else if (should_reset_buckets) {
5632        // a scrape during sleep already reported these buckets
5633        ctx_server.reset_metrics_bucket();
5634
5635        should_reset_buckets = false;
5636    }
5637}