@@ -231,7 +231,7 @@ bool Llama::add_message(LlamaIter &iter, const string &role, const string &conte
231231 int32_t n = 0 ;
232232
233233 if (_template.empty ()) {
234- _last_error = " No chat template available" ;
234+ set_last_error ( " No chat template available" ) ;
235235 return false ;
236236 }
237237
@@ -250,7 +250,7 @@ bool Llama::add_message(LlamaIter &iter, const string &role, const string &conte
250250 bool add_ass = (role == " user" || role == " tool" || role == " tool_result" );
251251 n = llama_chat_apply_template (_template.c_str (), &message, 1 , add_ass, buf.data (), buf_size);
252252 if (n < 0 ) {
253- _last_error = " No chat template no supported" ;
253+ set_last_error ( " No chat template no supported" ) ;
254254 return false ;
255255 } else if (n > (int32_t )buf.size ()) {
256256 buf.resize (n);
@@ -297,7 +297,7 @@ bool Llama::add_message(LlamaIter &iter, const string &role, const string &conte
297297
298298 llama_batch decoder_batch = llama_batch_get_one (&decoder_start_token_id, 1 );
299299 if (llama_decode (_ctx, decoder_batch)) {
300- _last_error = " Failed to evaluate decoder start token" ;
300+ set_last_error ( " Failed to evaluate decoder start token" ) ;
301301 return false ;
302302 }
303303 }
@@ -310,7 +310,7 @@ bool Llama::add_message(LlamaIter &iter, const string &role, const string &conte
310310
311311string Llama::next (LlamaIter &iter) {
312312 if (!iter._has_next ) {
313- _last_error = " Iteration beyond end of stream" ;
313+ set_last_error ( " Iteration beyond end of stream" ) ;
314314 return " " ;
315315 }
316316
@@ -328,7 +328,7 @@ string Llama::next(LlamaIter &iter) {
328328 // prepare the next batch with the sampled token
329329 llama_batch batch = llama_batch_get_one (&tok, 1 );
330330 if (llama_decode (_ctx, batch)) {
331- _last_error = " Failed to evaluate token during generation" ;
331+ set_last_error ( " Failed to evaluate token during generation" ) ;
332332 return " " ;
333333 }
334334
@@ -359,7 +359,7 @@ string Llama::all(LlamaIter &iter) {
359359 // decode the token
360360 llama_batch batch = llama_batch_get_one (&tok, 1 );
361361 if (llama_decode (_ctx, batch)) {
362- _last_error = " Failed to evaluate token during generation" ;
362+ set_last_error ( " Failed to evaluate token during generation" ) ;
363363 break ;
364364 }
365365 }
@@ -444,7 +444,7 @@ bool Llama::embed_text(const std::string &text, std::vector<float> &out, int emb
444444 int n_ctx = llama_n_ctx (_ctx);
445445 int n = tokens.size ();
446446 if (n > n_ctx) {
447- _last_error = std::format (" warning: chunk truncated {} -> {} tokens " , n, n_ctx);
447+ set_last_error ( std::format (" warning: chunk truncated {} -> {} tokens " , n, n_ctx) );
448448 n = n_ctx;
449449 tokens.resize (n);
450450 }
@@ -461,7 +461,7 @@ bool Llama::embed_text(const std::string &text, std::vector<float> &out, int emb
461461 }
462462
463463 if (!emb) {
464- _last_error = " no embedding returned\n " ;
464+ set_last_error ( " no embedding returned" ) ;
465465 return false ;
466466 }
467467
@@ -488,6 +488,25 @@ bool Llama::batch_decode_tokens(vector<llama_token> &tokens) {
488488 size_t batch_size = std::min ((size_t )n_batch, tokens.size () - i);
489489 llama_batch batch = llama_batch_get_one (tokens.data () + i, batch_size);
490490 int result = llama_decode (_ctx, batch);
491+ if (result == 1 ) {
492+ // KV full or fragmented mid-batch - evict oldest tokens and retry
493+ if (!make_space_for_tokens (n_batch)) {
494+ set_decode_error (result, i, tokens.size ());
495+ return false ;
496+ }
497+ result = llama_decode (_ctx, batch);
498+ if (result == 1 ) {
499+ // Eviction reported enough logical space but decode still failed -
500+ // this is fragmentation, not a real space shortage. No defrag API
501+ // is available, so fall back to a full non-system flush, which
502+ // guarantees one contiguous block.
503+ if (!full_flush_except_system ()) {
504+ set_decode_error (result, i, tokens.size ());
505+ return false ;
506+ }
507+ result = llama_decode (_ctx, batch);
508+ }
509+ }
491510 if (result != 0 ) {
492511 set_decode_error (result, i, tokens.size ());
493512 return false ;
@@ -504,7 +523,7 @@ bool Llama::configure_sampler() {
504523 if (!_grammar_src.empty ()) {
505524 llama_sampler *grammar = llama_sampler_init_grammar (_vocab, _grammar_src.c_str (), _grammar_root.c_str ());
506525 if (!grammar) {
507- _last_error = " failed to initialize grammar sampler" ;
526+ set_last_error ( " failed to initialize grammar sampler" ) ;
508527 return false ;
509528 }
510529 llama_sampler_chain_add (chain, grammar);
@@ -535,6 +554,21 @@ bool Llama::configure_sampler() {
535554 return true ;
536555}
537556
557+ bool Llama::full_flush_except_system () {
558+ llama_memory_t mem = llama_get_memory (_ctx);
559+ llama_pos pos_min = llama_memory_seq_pos_min (mem, 0 );
560+ if (pos_min < 0 ) {
561+ return true ; // already empty
562+ }
563+ llama_pos flush_start = pos_min + _n_system_tokens;
564+ bool ok = llama_memory_seq_rm (mem, 0 , flush_start, -1 );
565+ if (!ok) {
566+ set_last_error (" Failed to flush memory past system tokens" );
567+ return false ;
568+ }
569+ return true ;
570+ }
571+
538572// Makes space in the context for n_tokens by removing old tokens if necessary
539573// Returns true if successful, false if impossible to make space
540574//
@@ -549,7 +583,7 @@ bool Llama::configure_sampler() {
549583bool Llama::make_space_for_tokens (int n_tokens) {
550584 int n_ctx = llama_n_ctx (_ctx);
551585 if (n_tokens > n_ctx) {
552- _last_error = " Too many tokens, increase context size (n_ctx)" ;
586+ set_last_error ( " Too many tokens, increase context size (n_ctx)" ) ;
553587 return false ;
554588 }
555589
@@ -579,11 +613,11 @@ bool Llama::make_space_for_tokens(int n_tokens) {
579613 // Can't remove more than we have (minus _n_system_tokens)
580614 int removable = current_used - _n_system_tokens;
581615 if (tokens_to_remove > removable) {
582- _last_error = " Can't make enough space while keeping num_system_tokens tokens" ;
616+ set_last_error ( " Can't make enough space while keeping num_system_tokens tokens" ) ;
583617 return false ;
584618 }
585619 if (!_can_shift) {
586- _last_error = " Memory type doesn't support shifting, can't evict mid-sequence" ;
620+ set_last_error ( " Memory type doesn't support shifting, can't evict mid-sequence" ) ;
587621 return false ;
588622 }
589623
@@ -595,6 +629,7 @@ bool Llama::make_space_for_tokens(int n_tokens) {
595629 // Shift remaining tokens down
596630 llama_memory_seq_add (mem, 0 , remove_start + tokens_to_remove, -1 , -tokens_to_remove);
597631
632+ set_last_error (std::format (" made space for {} tokens" , n_tokens));
598633 return true ;
599634}
600635
@@ -603,13 +638,13 @@ vector<llama_token> Llama::tokenize(const string &prompt) {
603638
604639 int n_prompt = -llama_tokenize (_vocab, prompt.c_str (), prompt.size (), nullptr , 0 , true , true );
605640 if (n_prompt <= 0 ) {
606- _last_error = " Failed to tokenize prompt" ;
641+ set_last_error ( " Failed to tokenize prompt" ) ;
607642 } else {
608643 result.reserve (n_prompt);
609644 result.resize (n_prompt);
610645 if (llama_tokenize (_vocab, prompt.c_str (), prompt.size (),
611646 result.data (), n_prompt, true , true ) < 0 ) {
612- _last_error = " Failed to tokenize prompt" ;
647+ set_last_error ( " Failed to tokenize prompt" ) ;
613648 }
614649 }
615650 return result;
@@ -658,7 +693,7 @@ string Llama::token_to_string(LlamaIter &iter, llama_token tok) {
658693 return result;
659694}
660695
661- void Llama::set_last_error (const char * message) {
696+ void Llama::set_last_error (const string & message) {
662697 if (!_last_error.empty ()) {
663698 if (_last_error.back () == ' \n ' ) {
664699 _last_error.pop_back ();
@@ -679,10 +714,10 @@ void Llama::set_decode_error(int32_t error, int index, int num_tokens) {
679714 int space_needed = num_tokens;
680715 int space_available = n_ctx - current_used;
681716 _n_system_tokens;
682- _last_error = std::format (" KV exhausted. Reduce batch or context sizes. batchNo:{} requested:{} available:{}" ,
683- index, space_needed, space_available);
717+ set_last_error ( std::format (" KV exhausted. Reduce batch or context sizes. batchNo:{} requested:{} available:{}" ,
718+ index, space_needed, space_available) );
684719 } else {
685720 auto message = error == 2 ? " abort" : error == -1 ? " invalid" : " fatal" ;
686- _last_error = std::format (" Failed to decode batch. batchNo:{} error:'{}'" , index, message);
721+ set_last_error ( std::format (" Failed to decode batch. batchNo:{} error:'{}'" , index, message) );
687722 }
688723}
0 commit comments