1414
1515#include < algorithm>
1616#include < cassert>
17+ #include < climits>
1718#include < cstring>
1819#include < iomanip>
20+ #include < limits>
1921#include < map>
2022#include < unordered_map>
2123#include < cmath>
@@ -188,6 +190,24 @@ static bool common_dflash_argmax_token_valid(int32_t token_id, int n_vocab) {
188190 return token_id >= 0 && token_id < n_vocab;
189191}
190192
193+ static int common_dflash_invalid_reduced_logits_next_streak (
194+ int current_streak,
195+ bool valid_draft) {
196+ if (valid_draft) {
197+ return 0 ;
198+ }
199+
200+ return current_streak < INT_MAX ? current_streak + 1 : current_streak;
201+ }
202+
203+ static bool common_dflash_invalid_reduced_logits_fail_closed (
204+ int invalid_streak,
205+ int threshold) {
206+ return threshold > 0 && invalid_streak >= threshold;
207+ }
208+
209+ static constexpr int DFLASH_INVALID_REDUCED_LOGITS_FAIL_CLOSED_STREAK = 4 ;
210+
191211static bool common_dflash_argmax_shape_valid (
192212 const char * where,
193213 int rows_available,
@@ -2075,11 +2095,54 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
20752095 std::vector<int32_t > capture_layers;
20762096 bool target_capture_enabled = true ;
20772097 bool gpu_capture_available = false ;
2098+ int invalid_reduced_logits_streak = 0 ;
2099+ bool invalid_reduced_logits_fail_closed = false ;
20782100
20792101 bool profile_enabled (uint32_t flags) const {
20802102 return dflash_profile_has (profile_flags, flags);
20812103 }
20822104
2105+ void reset_invalid_reduced_logits_state () {
2106+ invalid_reduced_logits_streak = 0 ;
2107+ invalid_reduced_logits_fail_closed = false ;
2108+ }
2109+
2110+ void note_valid_reduced_logits_draft () {
2111+ reset_invalid_reduced_logits_state ();
2112+ }
2113+
2114+ void note_invalid_reduced_logits (
2115+ const char * where,
2116+ int32_t token_raw,
2117+ int row,
2118+ int rows,
2119+ int top_k,
2120+ int committed,
2121+ int cross_len,
2122+ int spec_idx,
2123+ int offset,
2124+ float score) {
2125+ invalid_reduced_logits_streak = common_dflash_invalid_reduced_logits_next_streak (
2126+ invalid_reduced_logits_streak, false );
2127+
2128+ LOG_ERR (" dflash: invalid reduced-logits token %d in %s at row=%d/%d "
2129+ " (top_k=%d committed=%d cross_len=%d ring_filled=%d ring_write_pos=%d "
2130+ " gpu_ring=%d cpu_ring_valid=%d invalid_reduced_logits_streak=%d score=%g spec=%d offset=%d)\n " ,
2131+ token_raw, where && where[0 ] ? where : " draft" , row, rows, top_k,
2132+ committed, cross_len, ring_filled, ring_write_pos,
2133+ gpu_ring_handle ? 1 : 0 , cpu_ring_valid ? 1 : 0 ,
2134+ invalid_reduced_logits_streak, (double ) score, spec_idx, offset);
2135+
2136+ if (common_dflash_invalid_reduced_logits_fail_closed (
2137+ invalid_reduced_logits_streak,
2138+ DFLASH_INVALID_REDUCED_LOGITS_FAIL_CLOSED_STREAK )) {
2139+ invalid_reduced_logits_fail_closed = true ;
2140+ LOG_ERR (" dflash: fail-closed after %d consecutive invalid reduced-logits drafts; "
2141+ " skipping DFlash drafting until the cross-ring state is reset\n " ,
2142+ invalid_reduced_logits_streak);
2143+ }
2144+ }
2145+
20832146 void discard_cross_ring (const char * reason) {
20842147 if (reason && reason[0 ]) {
20852148 LOG_WRN (" dflash: discarding cross-ring state: %s\n " , reason);
@@ -2096,6 +2159,7 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
20962159 prefill_suffix_seen = false ;
20972160 deferred_drafter_kv_tokens = 0 ;
20982161 ring_write_discarded = true ;
2162+ reset_invalid_reduced_logits_state ();
20992163 cross_buf.clear ();
21002164 common_dflash_reset_drafter_seq_and_kv_cache (ctx_dft, seq_id, reason);
21012165 }
@@ -2556,6 +2620,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
25562620 if (committed_len == 0 ) {
25572621 return -1 ;
25582622 }
2623+ if (invalid_reduced_logits_fail_closed) {
2624+ return -1 ;
2625+ }
25592626
25602627 flush_deferred_drafter_kv_cache (" batched draft" );
25612628 return build_cross_data (ctx_dft_ext);
@@ -2582,6 +2649,7 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
25822649 prefill_flush_requested = 0 ;
25832650 prefill_flush_written = 0 ;
25842651 prefill_suffix_seen = false ;
2652+ reset_invalid_reduced_logits_state ();
25852653 llama_dflash_prefill_capture_end (ctx_tgt);
25862654 return ;
25872655 }
@@ -2917,6 +2985,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
29172985 if (committed_len == 0 ) {
29182986 continue ;
29192987 }
2988+ if (invalid_reduced_logits_fail_closed) {
2989+ continue ;
2990+ }
29202991
29212992 const int64_t t0 = ggml_time_us ();
29222993
@@ -2984,8 +3055,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
29843055 }
29853056 const int32_t token_raw = argmax[i * K_flat];
29863057 if (!common_dflash_argmax_token_valid (token_raw, n_vocab)) {
2987- LOG_ERR (" dflash: invalid reduced-logits token %d in %s at row=%d/%d (top_k=%d committed=%d cross_len=%d)\n " ,
2988- token_raw, __func__, i, batch_len, K_flat, committed_len, cross_len);
3058+ const float score = argmax_probs ? argmax_probs[i * K_flat] : std::numeric_limits<float >::quiet_NaN ();
3059+ note_invalid_reduced_logits (__func__, token_raw, i, batch_len, K_flat,
3060+ committed_len, cross_len, -1 , 0 , score);
29893061 if (dp.draft_log_probs ) {
29903062 dp.draft_log_probs ->clear ();
29913063 }
@@ -3018,6 +3090,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
30183090 const int64_t t4 = ggml_time_us ();
30193091
30203092 n_draft_last = (int ) result.size ();
3093+ if (!result.empty ()) {
3094+ note_valid_reduced_logits_draft ();
3095+ }
30213096
30223097 if (profile_enabled (DFLASH_PROFILE_SUMMARY )) {
30233098 const llama_perf_context_data perf_dft = llama_perf_context (ctx_dft);
@@ -3048,6 +3123,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
30483123 if (n_draft <= 0 || committed_len == 0 ) {
30493124 return ;
30503125 }
3126+ if (invalid_reduced_logits_fail_closed) {
3127+ return ;
3128+ }
30513129
30523130 const int64_t t0 = ggml_time_us ();
30533131
@@ -3127,8 +3205,9 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
31273205 for (int d = 1 ; d <= depth_limit && d <= main_path_len && tree.n_nodes < tree_budget; ++d) {
31283206 const int32_t token_raw = argmax[d * K];
31293207 if (!common_dflash_argmax_token_valid (token_raw, n_vocab)) {
3130- LOG_ERR (" dflash tree: invalid reduced-logits token %d in %s at depth=%d/%d (top_k=%d)\n " ,
3131- token_raw, __func__, d, depth_limit, K);
3208+ const float score = argmax_probs ? argmax_probs[d * K] : std::numeric_limits<float >::quiet_NaN ();
3209+ note_invalid_reduced_logits (__func__, token_raw, d, depth_limit, K,
3210+ committed_len, cross_len, -1 , 0 , score);
31323211 break ;
31333212 }
31343213
@@ -3225,6 +3304,10 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
32253304 }
32263305
32273306 // build visibility matrix [(n_nodes+1) × (n_nodes+1)]
3307+ if (tree.n_nodes > 0 ) {
3308+ note_valid_reduced_logits_draft ();
3309+ }
3310+
32283311 int n = tree.n_nodes + 1 ;
32293312 tree.visibility .assign (n * n, false );
32303313 tree.visibility [0 ] = true ; // root sees itself
@@ -3536,6 +3619,7 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
35363619 ring_write_pos = 0 ;
35373620 ring_filled = 0 ;
35383621 committed_len = 0 ;
3622+ reset_invalid_reduced_logits_state ();
35393623 return ;
35403624 }
35413625
@@ -3556,6 +3640,7 @@ struct common_speculative_impl_dflash : public common_speculative_impl {
35563640 ring_write_pos = 0 ;
35573641 ring_filled = 0 ;
35583642 deferred_drafter_kv_tokens = 0 ;
3643+ reset_invalid_reduced_logits_state ();
35593644 common_dflash_reset_drafter_seq_and_kv_cache (ctx_dft, seq_id, " capture target hiddens" );
35603645 const int actual_written = ring_write (to_store, start_offset, true );
35613646 if (ring_write_discarded) {
@@ -4148,6 +4233,18 @@ bool common_dflash_tree_update_requires_cpu_hidden_for_test(
41484233 return !has_cpu_hidden && has_gpu_ring;
41494234}
41504235
4236+ int common_dflash_invalid_reduced_logits_next_streak_for_test (
4237+ int current_streak,
4238+ bool valid_draft) {
4239+ return common_dflash_invalid_reduced_logits_next_streak (current_streak, valid_draft);
4240+ }
4241+
4242+ bool common_dflash_invalid_reduced_logits_fail_closed_for_test (
4243+ int invalid_streak,
4244+ int threshold) {
4245+ return common_dflash_invalid_reduced_logits_fail_closed (invalid_streak, threshold);
4246+ }
4247+
41514248// ============================================================================
41524249// Fork-specific init and dispatch functions
41534250// ============================================================================
@@ -4540,8 +4637,10 @@ void common_speculative_draft_batch(
45404637 }
45414638 const int32_t token_raw = argmax[(offset + i) * K_flat];
45424639 if (!common_dflash_argmax_token_valid (token_raw, n_vocab)) {
4543- LOG_ERR (" dflash batch: invalid reduced-logits token %d in %s at spec=%d row=%d/%d (top_k=%d offset=%d)\n " ,
4544- token_raw, __func__, rs.spec_idx , i, batch_len, K_flat, offset);
4640+ const float score = argmax_probs ? argmax_probs[(offset + i) * K_flat] : std::numeric_limits<float >::quiet_NaN ();
4641+ auto * dfl = static_cast <common_speculative_impl_dflash *>(rs.impl );
4642+ dfl->note_invalid_reduced_logits (__func__, token_raw, i, batch_len, K_flat,
4643+ rs.draft_pos_base , rs.cross_len , rs.spec_idx , offset, score);
45454644 if (log_probs) {
45464645 log_probs->clear ();
45474646 }
@@ -4579,6 +4678,7 @@ void common_speculative_draft_batch(
45794678 }
45804679 }
45814680 if (!result.empty ()) {
4681+ dfl->note_valid_reduced_logits_draft ();
45824682 rs.impl ->n_gen_drafts ++;
45834683 rs.impl ->n_gen_tokens += result.size ();
45844684 specs[rs.spec_idx ]->curr_impl = rs.impl ;
0 commit comments