@@ -79,6 +79,8 @@ struct server_slot {
7979
8080 common_speculative * spec = nullptr ;
8181
82+ // TODO: move members that belong to the task (such as `generated_text`, `has_new_line`) to task_results_state
83+ // see https://github.com/ggml-org/llama.cpp/pull/18283#issuecomment-3710175837
8284 std::unique_ptr<const server_task> task;
8385 std::unique_ptr<const server_task> task_prev; // used for debugging
8486
@@ -153,7 +155,7 @@ struct server_slot {
153155
154156 common_sampler_ptr smpl;
155157
156- llama_token sampled; // in speculative mode, this is the last accepted token
158+ llama_token sampled; // in speculative mode, this is the last accepted token
157159 llama_tokens drafted;
158160
159161 // stats
@@ -201,12 +203,46 @@ struct server_slot {
201203 alora_invocation_start = -1 ;
202204 }
203205
206+ // remove cached prompt + tokens
207+ void clear (bool allow_processing) {
208+ if (!allow_processing) {
209+ GGML_ASSERT (!is_processing ());
210+ }
211+
212+ SLT_INF (*this , " clearing slot with %zu tokens\n " , prompt.tokens .size ());
213+
214+ llama_memory_seq_rm (llama_get_memory (ctx), id, -1 , -1 );
215+ prompt.tokens .clear ();
216+ }
217+
218+ void init_sampler () const {
219+ const int64_t t_start = ggml_time_us ();
220+
221+ common_sampler_reset (smpl.get ());
222+
223+ int n_text = 0 ;
224+
225+ for (int i = 0 ; i < (int ) prompt.tokens .size (); i++) {
226+ const llama_token id = prompt.tokens [i];
227+
228+ if (id != LLAMA_TOKEN_NULL ) {
229+ common_sampler_accept (smpl.get (), id, false );
230+ n_text++;
231+ }
232+ }
233+
234+ SLT_INF (*this , " init sampler, took %0.2f ms, tokens: text = %d, total = %d\n " ,
235+ (ggml_time_us () - t_start) / 1000.0 , n_text, (int ) prompt.tokens .size ());
236+ }
237+
238+ // TODO: move to server_task
204239 bool need_embd () const {
205240 GGML_ASSERT (task);
206241
207242 return server_task_type_need_embd (task->type );
208243 }
209244
245+ // TODO: move to server_task
210246 bool need_logits () const {
211247 GGML_ASSERT (task);
212248
@@ -258,10 +294,13 @@ struct server_slot {
258294 SLT_WRN (*this , " %s" , " slot is not processing\n " );
259295 return ;
260296 }
297+
261298 generated_token_probs.push_back (token);
262299 }
263300
264301 int get_n_draft_max () const {
302+ GGML_ASSERT (task);
303+
265304 if (!can_speculate ()) {
266305 return 0 ;
267306 }
@@ -287,12 +326,14 @@ struct server_slot {
287326 }
288327
289328 // note: a slot can also be either a parent or a child
329+ // TODO: move to server_task
290330 bool is_parent () const {
291- return is_processing () && task->n_children > 0 ;
331+ return task->n_children > 0 ;
292332 }
293333
334+ // TODO: move to server_task
294335 bool is_child () const {
295- return is_processing () && task->id_parent >= 0 ;
336+ return task->id_parent >= 0 ;
296337 }
297338
298339 void release () {
@@ -301,10 +342,16 @@ struct server_slot {
301342
302343 SLT_INF (*this , " stop processing: n_tokens = %d, truncated = %d\n " , prompt.n_tokens (), truncated);
303344
304- t_last_used = ggml_time_us ();
345+ t_last_used = ggml_time_us ();
305346 t_token_generation = (ggml_time_us () - t_start_generation) / 1e3 ;
347+
306348 state = SLOT_STATE_IDLE ;
307349
350+ // do not keep context of the child slots - the parent's context is enough
351+ if (is_child ()) {
352+ clear (false );
353+ }
354+
308355 task_prev = std::move (task);
309356 task.reset ();
310357
@@ -425,14 +472,22 @@ struct server_slot {
425472 }
426473
427474 void copy_state_to (server_slot & other) const {
428- llama_memory_seq_rm (llama_get_memory (ctx), other.id , 0 , -1 );
429- llama_memory_seq_cp (llama_get_memory (ctx), id, other.id , 0 , -1 );
475+ GGML_ASSERT (state == SLOT_STATE_DONE_PROMPT );
476+
477+ llama_memory_seq_rm (llama_get_memory (ctx), other.id , -1 , -1 );
478+ llama_memory_seq_cp (llama_get_memory (ctx), id, other.id , -1 , -1 );
479+
430480 other.n_decoded = n_decoded;
431481 other.n_remaining = n_remaining;
432482 other.i_batch = i_batch;
483+
484+ other.t_start_process_prompt = t_start_process_prompt;
485+ other.t_prompt_processing = t_prompt_processing;
433486 other.n_prompt_tokens_cache = n_prompt_tokens_cache;
434487 other.n_prompt_tokens_processed = n_prompt_tokens_processed;
488+
435489 other.prompt = prompt.clone ();
490+ other.init_sampler ();
436491 }
437492};
438493
@@ -745,6 +800,7 @@ struct server_context_impl {
745800 }
746801
747802 slots.clear ();
803+
748804 for (int i = 0 ; i < params_base.n_parallel ; i++) {
749805 server_slot slot;
750806
@@ -993,7 +1049,7 @@ struct server_context_impl {
9931049 ret->prompt_save (*prompt_cache);
9941050
9951051 if (!ret->prompt_load (*prompt_cache, task.tokens )) {
996- clear_slot (* ret);
1052+ ret-> clear ( false );
9971053 }
9981054
9991055 prompt_cache->update ();
@@ -1005,17 +1061,6 @@ struct server_context_impl {
10051061 return ret;
10061062 }
10071063
1008- void clear_slot (server_slot & slot, bool allow_processing = false ) const {
1009- if (!allow_processing) {
1010- GGML_ASSERT (!slot.is_processing ());
1011- }
1012-
1013- SLT_WRN (slot, " clearing slot with %zu tokens\n " , slot.prompt .tokens .size ());
1014-
1015- llama_memory_seq_rm (llama_get_memory (ctx), slot.id , -1 , -1 );
1016- slot.prompt .tokens .clear ();
1017- }
1018-
10191064 // return true if at least one slot has been cleared
10201065 // TODO: improve logic
10211066 // - smarter decision which slot to clear (LRU or longest prompt?)
@@ -1036,7 +1081,7 @@ struct server_context_impl {
10361081 if (slot.prompt .n_tokens () > 0 ) {
10371082 SRV_WRN (" purging slot %d with %zu tokens\n " , slot.id , slot.prompt .tokens .size ());
10381083
1039- clear_slot ( slot);
1084+ slot. clear ( false );
10401085
10411086 res = true ;
10421087
@@ -1182,7 +1227,7 @@ struct server_context_impl {
11821227 ? SLOT_STATE_WAIT_OTHER // wait for the parent to process prompt
11831228 : SLOT_STATE_STARTED ;
11841229
1185- SLT_INF (slot, " %s " , " processing task\n " );
1230+ SLT_INF (slot, " processing task, is_child = %d \n " , slot. is_child () );
11861231
11871232 return true ;
11881233 }
@@ -1819,7 +1864,7 @@ struct server_context_impl {
18191864 // Erase token cache
18201865 const size_t n_erased = slot->prompt .tokens .size ();
18211866
1822- clear_slot (* slot);
1867+ slot-> clear ( false );
18231868
18241869 auto res = std::make_unique<server_task_result_slot_erase>();
18251870 res->id = task.id ;
@@ -2053,8 +2098,29 @@ struct server_context_impl {
20532098 continue ;
20542099 }
20552100
2101+ // check if this is a child slot
2102+ if (slot.state == SLOT_STATE_WAIT_OTHER ) {
2103+ SLT_DBG (slot, " %s" , " waiting for parent slot to complete\n " );
2104+ continue ;
2105+ }
2106+
20562107 // this slot still has a prompt to be processed
20572108 if (slot.state == SLOT_STATE_PROCESSING_PROMPT || slot.state == SLOT_STATE_STARTED ) {
2109+ // wait for all children to be launched
2110+ if (slot.is_parent ()) {
2111+ int n_launched = 0 ;
2112+ for (auto & other : slots) {
2113+ if (other.is_processing () && other.is_child () && other.task ->id_parent == slot.task ->id ) {
2114+ ++n_launched;
2115+ }
2116+ }
2117+
2118+ if (n_launched < slot.task ->n_children ) {
2119+ SLT_DBG (slot, " waiting for children to be launched, n_children = %d, n_launched = %d\n " , slot.task ->n_children , n_launched);
2120+ continue ;
2121+ }
2122+ }
2123+
20582124 const auto & input_tokens = slot.task ->tokens ;
20592125
20602126 // TODO: maybe move branch to outside of this loop in the future
@@ -2355,7 +2421,7 @@ struct server_context_impl {
23552421 if (!llama_memory_seq_rm (llama_get_memory (ctx), slot.id , p0, -1 )) {
23562422 SLT_WRN (slot, " failed to truncate tokens with position >= %d - clearing the memory\n " , p0);
23572423
2358- clear_slot ( slot, /* allow_processing= */ true );
2424+ slot. clear ( true );
23592425
23602426 // there is no common part left
23612427 slot.n_prompt_tokens_cache = 0 ;
@@ -2455,16 +2521,6 @@ struct server_context_impl {
24552521
24562522 GGML_ASSERT (batch.n_tokens > 0 );
24572523
2458- common_sampler_reset (slot.smpl .get ());
2459-
2460- // Process all prompt tokens through sampler system
2461- for (int i = 0 ; i < slot.task ->n_tokens (); ++i) {
2462- llama_token id = input_tokens[i];
2463- if (id != LLAMA_TOKEN_NULL ) {
2464- common_sampler_accept (slot.smpl .get (), id, false );
2465- }
2466- }
2467-
24682524 // extract the logits only for the last token
24692525 batch.logits [batch.n_tokens - 1 ] = true ;
24702526
@@ -2473,6 +2529,8 @@ struct server_context_impl {
24732529
24742530 SLT_INF (slot, " prompt done, n_tokens = %d, batch.n_tokens = %d\n " , slot.prompt .n_tokens (), batch.n_tokens );
24752531
2532+ slot.init_sampler ();
2533+
24762534 const auto pos_min = llama_memory_seq_pos_min (llama_get_memory (ctx), slot.id );
24772535 const auto pos_max = llama_memory_seq_pos_max (llama_get_memory (ctx), slot.id );
24782536
@@ -2519,11 +2577,6 @@ struct server_context_impl {
25192577 }
25202578 }
25212579
2522- if (batch.n_tokens == 0 ) {
2523- SRV_WRN (" %s" , " no tokens to decode\n " );
2524- return ;
2525- }
2526-
25272580 SRV_DBG (" decoding batch, n_tokens = %d\n " , batch.n_tokens );
25282581
25292582 if (slot_batched) {
@@ -2540,6 +2593,10 @@ struct server_context_impl {
25402593 llama_set_embeddings (ctx, slot_batched->need_embd ());
25412594 }
25422595
2596+ if (batch.n_tokens == 0 ) {
2597+ SRV_WRN (" %s" , " no tokens to decode\n " );
2598+ }
2599+
25432600 int32_t i_next = 0 ;
25442601
25452602 // process the created batch of tokens
@@ -2591,7 +2648,7 @@ struct server_context_impl {
25912648
25922649 // note: it's complicated to keep track of how much of the current batch has been
25932650 // processed before the error occurred, so we simply clear the entire context
2594- clear_slot ( slot);
2651+ slot. clear ( false );
25952652 }
25962653 }
25972654
@@ -2615,27 +2672,34 @@ struct server_context_impl {
26152672 // on successful decode, restore the original batch size
26162673 n_batch = llama_n_batch (ctx);
26172674
2675+ // handle `n_cmpl > 1` tasks - when the main prompt is processed, activate all child tasks too
26182676 for (auto & slot : slots) {
2619- // may need to copy state to other slots
26202677 if (slot.state == SLOT_STATE_DONE_PROMPT && slot.is_parent ()) {
2621- std::vector<server_slot *> child_slots;
2678+ SLT_INF (slot, " parent task prompt done, n_children = %d\n " , slot.task ->n_children );
2679+
2680+ std::vector<server_slot *> children;
26222681 for (auto & other : slots) {
26232682 if (other.state == SLOT_STATE_WAIT_OTHER && slot.task ->id == other.task ->id_parent ) {
2624- child_slots .push_back (&other);
2683+ children .push_back (&other);
26252684 }
26262685 }
26272686
26282687 // we can only proceed if all child slots are having the correct tasks
2629- if (child_slots. size () == slot.task ->n_children ) {
2688+ if (slot.task ->n_children == ( int ) children. size () ) {
26302689 // copy state to the child slots
2631- for (auto & child : child_slots) {
2632- SLT_INF (slot, " copying state to child %d\n " , child->id );
2690+ for (auto & child : children) {
2691+ SLT_INF (slot, " - copying state to child %d\n " , child->id );
2692+
2693+ GGML_ASSERT (child->state == SLOT_STATE_WAIT_OTHER );
2694+
26332695 slot.copy_state_to (*child);
26342696 child->state = SLOT_STATE_DONE_PROMPT ;
26352697 }
26362698 }
26372699 }
2700+ }
26382701
2702+ for (auto & slot : slots) {
26392703 // optionally send prompt processing progress
26402704 if (slot.state == SLOT_STATE_PROCESSING_PROMPT || slot.state == SLOT_STATE_DONE_PROMPT ) {
26412705 if (slot.task ->params .stream && slot.task ->params .return_progress ) {
@@ -2720,7 +2784,7 @@ struct server_context_impl {
27202784 continue ;
27212785 }
27222786
2723- size_t n_draft = slot.drafted .size ();
2787+ const size_t n_draft = slot.drafted .size ();
27242788
27252789 // the accepted tokens from the speculation
27262790 const auto ids = common_sampler_sample_and_accept_n (slot.smpl .get (), ctx, slot.i_batch_dft , slot.drafted );
@@ -2923,9 +2987,11 @@ std::unique_ptr<server_res_generator> server_routes::handle_completions_impl(
29232987 task.params .oaicompat_cmpl_id = completion_id;
29242988 task.params .oaicompat_model = meta->model_name ;
29252989
2990+ // prepare child tasks
29262991 if (task.params .n_cmpl > 1 ) {
29272992 task.n_children = task.params .n_cmpl - 1 ;
2928- for (size_t j = 0 ; j < task.n_children ; j++) {
2993+
2994+ for (int j = 0 ; j < task.n_children ; j++) {
29292995 server_task child = task.create_child (task.id , rd.get_new_id ());
29302996
29312997 // use different sampling seed for each child
@@ -2938,7 +3004,8 @@ std::unique_ptr<server_res_generator> server_routes::handle_completions_impl(
29383004 }
29393005 }
29403006
2941- tasks.push_back (std::move (task));
3007+ // note: the parent task always launches first
3008+ tasks.insert (tasks.begin (), std::move (task));
29423009 }
29433010
29443011 rd.post_tasks (std::move (tasks));
0 commit comments