@@ -835,6 +835,14 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
835835
836836 // scratch buffer for concatenated target features [n_tokens, n_embd_enc]
837837 std::vector<float > features_buf;
838+ // Stashed encoder outputs for deferred KV cache injection
839+ struct StashedG {
840+ llama_pos pos;
841+ std::vector<float > data; // n_embd_dec floats
842+ };
843+ std::vector<std::vector<StashedG>> stashed; // [n_seq][...]
844+ static constexpr int32_t MAX_STASH = 64 ;
845+ bool m_use_deferred = true ;
838846
839847 common_speculative_impl_draft_dflash (const common_params_speculative & params, uint32_t n_seq)
840848 : common_speculative_impl(COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH , n_seq)
@@ -869,7 +877,17 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
869877 LOG_INF (" %s: - n_max=%d, n_min=%d, p_min=%.2f\n " , __func__, this ->params .n_max , this ->params .n_min , this ->params .p_min );
870878 LOG_INF (" %s: - block_size=%d, mask_token_id=%d, n_extract=%u\n " , __func__, block_size, mask_token_id, target_layer_ids_n);
871879
872- // DFlash input is [id_last, <mask> * (block_size-1)], so it can draft at most block_size-1 tokens per step
880+ // Detect target model type: Gemma4 models regress with deferred injection (low acceptance rate)
881+ {
882+ char model_desc[128 ] = {};
883+ llama_model_desc (model_tgt, model_desc, sizeof (model_desc));
884+ if (strstr (model_desc, " gemma" )) {
885+ m_use_deferred = false ;
886+ LOG_INF (" %s: - deferred_kv_injection=0 (disabled for %s)\n " , __func__, model_desc);
887+ } else {
888+ LOG_INF (" %s: - deferred_kv_injection=1 (enabled for %s)\n " , __func__, model_desc);
889+ }
890+ }
873891 if (this ->params .n_max > block_size - 1 || this ->params .n_min > block_size - 1 ) {
874892 LOG_WRN (" %s: requested draft size (n_max=%d, n_min=%d) exceeds the trained DFlash block size %d -- clamping to %d\n " ,
875893 __func__, this ->params .n_max , this ->params .n_min , block_size, block_size - 1 );
@@ -880,6 +898,8 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
880898 batch = llama_batch_init (llama_n_batch (ctx_dft), 0 , n_seq);
881899 batch_inject = llama_batch_init (llama_n_batch (ctx_dft), n_embd_dec, n_seq);
882900
901+ stashed.resize (n_seq);
902+
883903 smpls.resize (n_seq);
884904 for (auto & s : smpls) {
885905 common_params_sampling sparams;
@@ -889,13 +909,13 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
889909 s.reset (common_sampler_init (model_dft, sparams));
890910 }
891911
892- // turn on extraction of the target layers' input embeddings
912+ // Enable extraction of target model layer outputs for DFlash encoder
893913 for (uint32_t k = 0 ; k < target_layer_ids_n; ++k) {
894914 llama_set_embeddings_layer_inp (ctx_tgt, (uint32_t ) target_layer_ids[k], true );
895915 }
896916
917+ // Enable nextn (encoder) output on the draft context for DFlash decoder K/V injection
897918 llama_set_embeddings_nextn (ctx_dft, true , /* masked*/ true );
898- llama_set_causal_attn (ctx_dft, false ); // DFlash needs non-causal attention
899919 }
900920
901921 ~common_speculative_impl_draft_dflash () override {
@@ -996,31 +1016,76 @@ struct common_speculative_impl_draft_dflash : public common_speculative_impl {
9961016 const float * inp_g = llama_get_embeddings_nextn (ctx_dft);
9971017 GGML_ASSERT (inp_g && " DFlash encoder produced no output." );
9981018
999- // inject the DFlash decoder K/V cache at the tokens' target positions
1000- batch_inject.n_tokens = n_chunk;
1001- std::memcpy (batch_inject.embd , inp_g, (size_t ) n_chunk * n_embd_dec * sizeof (float ));
1002-
1003- for (int32_t i = 0 ; i < n_chunk; ++i) {
1004- batch_inject.pos [i] = batch_in.pos [i_batch_beg[seq_id] + offset + i];
1005- batch_inject.n_seq_id [i] = 1 ;
1006- batch_inject.seq_id [i][0 ] = seq_id;
1007- batch_inject.logits [i] = false ;
1008- }
1009- rc = llama_decode (ctx_dft, batch_inject);
1010- if (rc != 0 ) {
1011- LOG_ERR (" %s: llama_decode(ctx_dft) failed rc=%d (n_tokens=%d, offset=%d)\n " ,
1012- __func__, rc, (int ) n_chunk, (int ) offset);
1013- return false ;
1019+ if (m_use_deferred) {
1020+ // stash encoder output for deferred KV cache injection
1021+ auto & stash = stashed[seq_id];
1022+ for (int32_t i = 0 ; i < n_chunk; ++i) {
1023+ StashedG sg;
1024+ sg.pos = batch_in.pos [i_batch_beg[seq_id] + offset + i];
1025+ sg.data .resize (n_embd_dec);
1026+ std::copy (inp_g + (size_t ) i * n_embd_dec, inp_g + (size_t ) (i + 1 ) * n_embd_dec, sg.data .begin ());
1027+ stash.push_back (std::move (sg));
1028+ }
1029+ // auto-flush to prevent unbounded accumulation
1030+ if ((int32_t ) stash.size () >= MAX_STASH ) {
1031+ flush_injection (seq_id);
1032+ }
1033+ } else {
1034+ // immediate injection (original per-chunk behavior)
1035+ auto & stash = stashed[seq_id];
1036+ for (int32_t i = 0 ; i < n_chunk; ++i) {
1037+ StashedG sg;
1038+ sg.pos = batch_in.pos [i_batch_beg[seq_id] + offset + i];
1039+ sg.data .resize (n_embd_dec);
1040+ std::copy (inp_g + (size_t ) i * n_embd_dec, inp_g + (size_t ) (i + 1 ) * n_embd_dec, sg.data .begin ());
1041+ stash.push_back (std::move (sg));
1042+ }
1043+ flush_injection (seq_id);
10141044 }
10151045 }
10161046 }
10171047
10181048 return true ;
10191049 }
10201050
1051+ // Batch-inject all stashed encoder outputs into the draft decoder's KV cache
1052+ void flush_injection (llama_seq_id seq_id) {
1053+ auto & stash = stashed[seq_id];
1054+ if (stash.empty ()) return ;
1055+
1056+ const int32_t n = (int32_t ) stash.size ();
1057+ batch_inject.n_tokens = n;
1058+ for (int32_t i = 0 ; i < n; ++i) {
1059+ std::memcpy (batch_inject.embd + (size_t ) i * n_embd_dec,
1060+ stash[i].data .data (), (size_t ) n_embd_dec * sizeof (float ));
1061+ batch_inject.pos [i] = stash[i].pos ;
1062+ batch_inject.n_seq_id [i] = 1 ;
1063+ batch_inject.seq_id [i][0 ] = seq_id;
1064+ batch_inject.logits [i] = false ;
1065+ }
1066+
1067+ auto * ctx_dft = params.ctx_dft ;
1068+ int32_t rc = llama_decode (ctx_dft, batch_inject);
1069+ if (rc != 0 ) {
1070+ LOG_ERR (" %s: flush_injection llama_decode(ctx_dft) failed rc=%d (n_tokens=%d)\n " ,
1071+ __func__, rc, n);
1072+ }
1073+
1074+ stash.clear ();
1075+ }
1076+
10211077 void draft (common_speculative_draft_params_vec & dparams) override {
10221078 auto & ctx_dft = params.ctx_dft ;
10231079
1080+ if (m_use_deferred) {
1081+ // flush all stashed KV injections before drafting
1082+ for (llama_seq_id seq_id = 0 ; seq_id < (llama_seq_id) n_seq; ++seq_id) {
1083+ if (dparams[seq_id].drafting ) {
1084+ flush_injection (seq_id);
1085+ }
1086+ }
1087+ }
1088+
10241089 common_batch_clear (batch);
10251090
10261091 // build one batch holding every drafting sequence's noise block into a single decode)
@@ -2082,6 +2147,13 @@ common_speculative * common_speculative_init(common_params_speculative & params,
20822147 bool has_mtp = (enabled_configs & (1u << COMMON_SPECULATIVE_TYPE_DRAFT_MTP )) && params.draft .ctx_dft != nullptr ;
20832148 bool has_draft_dflash = (enabled_configs & (1u << COMMON_SPECULATIVE_TYPE_DRAFT_DFLASH )) && params.draft .ctx_dft != nullptr ;
20842149
2150+ // If --dflash or --eagle3 flags are set, enable the corresponding type
2151+ if (!has_draft_dflash && params.draft .dflash && params.draft .ctx_dft != nullptr ) {
2152+ has_draft_dflash = true ;
2153+ }
2154+ if (!has_draft_eagle3 && params.draft .eagle3 && params.draft .ctx_dft != nullptr ) {
2155+ has_draft_eagle3 = true ;
2156+ }
20852157
20862158
20872159 bool has_ngram_cache = (enabled_configs & (1u << COMMON_SPECULATIVE_TYPE_NGRAM_CACHE ));
0 commit comments