Skip to content

Commit 5743828

Browse files
committed
eagle3: add recurrent verification state support
1 parent 5bb2d50 commit 5743828

13 files changed

Lines changed: 634 additions & 50 deletions

convert_hf_to_gguf.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2748,6 +2748,7 @@ def prepare_tensors(self):
27482748
"LlavaForConditionalGeneration",
27492749
"VoxtralForConditionalGeneration",
27502750
"LlamaForCausalLMEagle3",
2751+
"Eagle3LlamaForCausalLM",
27512752
"Eagle3Speculator",
27522753
"Eagle3DraftModel",
27532754
"IQuestCoderForCausalLM",
@@ -2786,7 +2787,9 @@ def __init__(self, *args, **kwargs):
27862787
target_config = json.load(f)
27872788

27882789
# EAGLE3 extract_layers
2789-
target_num_layers = target_config["num_hidden_layers"]
2790+
# Support nested config (e.g. VL/MoE models with text_config)
2791+
tc = target_config.get("text_config", target_config)
2792+
target_num_layers = tc["num_hidden_layers"]
27902793
extract_layers = [2, target_num_layers // 2, target_num_layers - 3]
27912794
logger.info(f"EAGLE3: extract_layers = {extract_layers} (target model has {target_num_layers} layers)")
27922795
self.gguf_writer.add_array(f"{self.gguf_writer.arch}.extract_layers", extract_layers)
@@ -2796,7 +2799,7 @@ def __init__(self, *args, **kwargs):
27962799
target_hidden_size = eagle3_raw_config["target_hidden_size"]
27972800
logger.info(f"EAGLE3: target_hidden_size = {target_hidden_size} (from EAGLE3 config)")
27982801
else:
2799-
target_hidden_size = target_config["hidden_size"]
2802+
target_hidden_size = tc["hidden_size"]
28002803
logger.info(f"EAGLE3: target_hidden_size = {target_hidden_size} (from target model config)")
28012804
self.gguf_writer.add_uint32(f"{self.gguf_writer.arch}.target_hidden_size", target_hidden_size)
28022805

examples/speculative-simple/speculative-simple.cpp

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "llama.h"
77
#include "chat.h"
88

9+
#include <algorithm>
910
#include <clocale>
1011
#include <cstdio>
1112
#include <cstring>
@@ -33,6 +34,9 @@ int main(int argc, char ** argv) {
3334
return 1;
3435
}
3536

37+
params.kv_unified = true;
38+
params.n_parallel = std::max(params.n_parallel, params.speculative.n_max + 2);
39+
3640
// init llama.cpp
3741
llama_backend_init();
3842
llama_numa_init(params.numa);
@@ -111,7 +115,7 @@ int main(int argc, char ** argv) {
111115
int n_predict = 0;
112116
int n_drafted = 0;
113117
int n_accept = 0;
114-
118+
int draft_len_slot[10] = {0};
115119
// used to determine end of generation
116120
bool has_eos = false;
117121

@@ -150,6 +154,7 @@ int main(int argc, char ** argv) {
150154
llama_token id_last;
151155
llama_tokens prompt_tgt;
152156
int n_past;
157+
bool pending_first_token_print = false;
153158

154159
// TODO: simplify
155160
if (params.speculative.eagle3) {
@@ -158,7 +163,7 @@ int main(int argc, char ** argv) {
158163

159164
id_last = common_sampler_sample(smpl, ctx_tgt, -1);
160165
common_sampler_accept(smpl, id_last, true);
161-
LOG("%s", common_token_to_piece(ctx_tgt, id_last).c_str());
166+
pending_first_token_print = true;
162167
n_predict++;
163168

164169
// all tokens currently in the target context
@@ -179,14 +184,18 @@ int main(int argc, char ** argv) {
179184
n_past = inp.size() - 1;
180185
}
181186

182-
// init the speculator
183187
const auto & params_spec = params.speculative;
184-
185188
struct common_speculative * spec = common_speculative_init(params.speculative, ctx_tgt);
186189

187190
common_speculative_begin(spec, prompt_tgt);
188191

192+
if (pending_first_token_print) {
193+
LOG("%s", common_token_to_piece(ctx_tgt, id_last).c_str());
194+
pending_first_token_print = false;
195+
}
196+
189197
llama_batch batch_tgt = llama_batch_init(llama_n_batch(ctx_tgt), 0, 1);
198+
llama_memory_t mem_tgt = llama_get_memory(ctx_tgt);
190199

191200
const auto t_enc_end = ggml_time_us();
192201

@@ -200,37 +209,34 @@ int main(int argc, char ** argv) {
200209
// offloaded to a remote device. it doesn't even have to be based on an LLM. instead, it can provide tokens
201210
// from a cache or lookup tables.
202211
//
212+
213+
203214
llama_tokens draft = common_speculative_draft(spec, params_spec, prompt_tgt, id_last);
204215

205216
//LOG_DBG("draft: %s\n", string_from(ctx_dft, draft).c_str());
206217

207-
// always have a token to evaluate from before - id_last
208-
common_batch_clear(batch_tgt);
209-
common_batch_add (batch_tgt, id_last, n_past++, { 0 }, true);
218+
// do not waste time on small drafts
219+
if (draft.size() < (size_t) params_spec.n_min) {
220+
draft.clear();
221+
}
210222

211-
// evaluate the target model on [id_last, draft0, draft1, ..., draftN-1]
212-
{
213-
// do not waste time on small drafts
214-
if (draft.size() < (size_t) params_spec.n_min) {
215-
draft.clear();
216-
}
223+
const int n_verify = (int) draft.size() + 1;
217224

218-
for (size_t i = 0; i < draft.size(); ++i) {
219-
common_batch_add(batch_tgt, draft[i], n_past + i, { 0 }, true);
220-
}
225+
common_batch_clear(batch_tgt);
221226

222-
//LOG_DBG("target batch: %s\n", string_from(ctx_tgt, batch_tgt).c_str());
227+
if (!llama_memory_eagle3_recurrent_begin(mem_tgt, 0, n_verify, n_past)) {
228+
LOG_ERR("%s: failed to reserve EAGLE3 recurrent verification slots for depth %d\n", __func__, n_verify);
229+
return 1;
230+
}
223231

224-
llama_decode(ctx_tgt, batch_tgt);
232+
common_batch_add(batch_tgt, id_last, n_past++, { 0 }, true);
233+
234+
for (size_t i = 0; i < draft.size(); ++i) {
235+
common_batch_add(batch_tgt, draft[i], n_past + i, { 0 }, true);
225236
}
226237

227-
// sample from the full target batch and return the accepted tokens based on the target sampler
228-
//
229-
// for each token to be accepted, the sampler would have to sample that same token
230-
// in such cases, instead of decoding the sampled token as we normally do, we simply continue with the
231-
// available logits from the batch and sample the next token until we run out of logits or the sampler
232-
// disagrees with the draft
233-
//
238+
llama_decode(ctx_tgt, batch_tgt);
239+
234240
const auto ids = common_sampler_sample_and_accept_n(smpl, ctx_tgt, draft);
235241

236242
//LOG_DBG("ids: %s\n", string_from(ctx_tgt, ids).c_str());
@@ -240,8 +246,11 @@ int main(int argc, char ** argv) {
240246
n_past += ids.size() - 1;
241247
n_drafted += draft.size(); // note: we ignore the discarded small drafts
242248
n_accept += ids.size() - 1;
249+
draft_len_slot[ids.size()-1] += 1;
243250
n_predict += ids.size();
244251

252+
common_speculative_accept(spec, ids.size() - 1);
253+
245254
// process the accepted tokens and update contexts
246255
//
247256
// this is the standard token post-processing that we normally do
@@ -268,10 +277,11 @@ int main(int argc, char ** argv) {
268277

269278
LOG_DBG("accepted %d/%d draft tokens, the last target token is: (%d)\n", (int) ids.size() - 1, (int) draft.size(), id_last);
270279

280+
GGML_ASSERT(llama_memory_eagle3_recurrent_promote(mem_tgt, 0, ids.size()));
281+
271282
{
272283
LOG_DBG("clear kv cache from any extra tokens, n_past = %d\n", n_past);
273-
274-
llama_memory_seq_rm(llama_get_memory(ctx_tgt), 0, n_past, -1);
284+
llama_memory_seq_rm(mem_tgt, 0, n_past, -1);
275285
}
276286

277287
if ((params.n_predict >= 0 && n_predict > params.n_predict) || has_eos) {
@@ -293,6 +303,13 @@ int main(int argc, char ** argv) {
293303
LOG_INF("n_predict = %d\n", n_predict);
294304
LOG_INF("n_drafted = %d\n", n_drafted);
295305
LOG_INF("n_accept = %d\n", n_accept);
306+
307+
308+
for(int j=0;j<10;j++){
309+
LOG_INF("draft_len_slot[%d] = %d\n",j,draft_len_slot[j]);
310+
311+
}
312+
296313
LOG_INF("accept = %.3f%%\n", 100.0f * n_accept / n_drafted);
297314

298315
LOG_INF("\n");

examples/speculative/speculative.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ int main(int argc, char ** argv) {
188188
int n_predict = 0;
189189
int n_drafted = 0;
190190
int n_accept = 0;
191+
int n_seq_accept[10] = {0};
191192

192193
int n_past_tgt = inp.size();
193194
int n_past_dft = inp.size();
@@ -381,13 +382,14 @@ int main(int argc, char ** argv) {
381382
token_str = common_token_to_piece(ctx_tgt, token_id);
382383

383384
for (int s = 0; s < n_seq_dft; ++s) {
385+
// n_seq_accept
384386
if (!drafts[s].active) {
385387
continue;
386388
}
387389

388390
if (i_dft < (int) drafts[s].tokens.size() && token_id == drafts[s].tokens[i_dft]) {
389391
LOG_DBG("the sampled target token matches the %dth drafted token of sequence %d (%d, '%s') - accepted\n", i_dft, s, token_id, token_str.c_str());
390-
392+
n_seq_accept[i_dft] += 1;
391393
s_keep = s;
392394
accept = true;
393395
} else {
@@ -625,6 +627,13 @@ int main(int argc, char ** argv) {
625627
LOG_INF("n_predict = %d\n", n_predict);
626628
LOG_INF("n_drafted = %d\n", n_drafted);
627629
LOG_INF("n_accept = %d\n", n_accept);
630+
LOG_INF("n_accept = %d\n", n_accept);
631+
for (int i = 0; i < 10; i++)
632+
{
633+
LOG_INF("n_seq_accept[%d]-accept_len-%d\n", i,n_seq_accept[i]);
634+
635+
}
636+
628637
LOG_INF("accept = %.3f%%\n", 100.0f * n_accept / n_drafted);
629638

630639
LOG_INF("\n");

include/llama.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,26 @@ extern "C" {
777777
// Check if the memory supports shifting
778778
LLAMA_API bool llama_memory_can_shift(llama_memory_t mem);
779779

780+
// Begin a transient EAGLE3 recurrent verification round for a live sequence.
781+
// The round reserves one recurrent state cell per verification depth, starting at position p0.
782+
LLAMA_API bool llama_memory_eagle3_recurrent_begin(
783+
llama_memory_t mem,
784+
llama_seq_id live_seq_id,
785+
uint32_t n_depth,
786+
llama_pos p0);
787+
788+
// Promote the accepted verification depth back to the live sequence.
789+
// Returns false if no active EAGLE3 recurrent round exists for the sequence.
790+
LLAMA_API bool llama_memory_eagle3_recurrent_promote(
791+
llama_memory_t mem,
792+
llama_seq_id live_seq_id,
793+
uint32_t depth);
794+
795+
// Clear any transient EAGLE3 recurrent verification state for the live sequence.
796+
LLAMA_API void llama_memory_eagle3_recurrent_clear(
797+
llama_memory_t mem,
798+
llama_seq_id live_seq_id);
799+
780800
//
781801
// State / sessions
782802
//

src/llama-context.cpp

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,11 +2193,20 @@ ggml_cgraph * llama_context::graph_reserve(
21932193

21942194
// EAGLE3: auto-detect encoder (embeddings+no target_model) or decoder (has target_model)
21952195
llm_graph_type gtype = LLM_GRAPH_TYPE_DEFAULT;
2196+
// if (model.arch == LLM_ARCH_EAGLE3) {
2197+
// if (cparams.embeddings && model.target_tok_embd == nullptr) {
2198+
// gtype = LLM_GRAPH_TYPE_ENCODER;
2199+
// } else if (model.target_tok_embd != nullptr) {
2200+
// gtype = LLM_GRAPH_TYPE_DECODER;
2201+
// }
2202+
// }
2203+
2204+
// 修改后:
21962205
if (model.arch == LLM_ARCH_EAGLE3) {
2197-
if (cparams.embeddings && model.target_tok_embd == nullptr) {
2198-
gtype = LLM_GRAPH_TYPE_ENCODER;
2199-
} else if (model.target_tok_embd != nullptr) {
2206+
if (model.target_tok_embd != nullptr) {
22002207
gtype = LLM_GRAPH_TYPE_DECODER;
2208+
} else {
2209+
gtype = LLM_GRAPH_TYPE_ENCODER;
22012210
}
22022211
}
22032212
const auto gparams = graph_params(res, ubatch, mctx, gtype);
@@ -3468,6 +3477,39 @@ bool llama_memory_can_shift(llama_memory_t mem) {
34683477
return mem->get_can_shift();
34693478
}
34703479

3480+
bool llama_memory_eagle3_recurrent_begin(
3481+
llama_memory_t mem,
3482+
llama_seq_id live_seq_id,
3483+
uint32_t n_depth,
3484+
llama_pos p0) {
3485+
if (!mem) {
3486+
return false;
3487+
}
3488+
3489+
return mem->eagle3_recurrent_round_begin(live_seq_id, n_depth, p0);
3490+
}
3491+
3492+
bool llama_memory_eagle3_recurrent_promote(
3493+
llama_memory_t mem,
3494+
llama_seq_id live_seq_id,
3495+
uint32_t depth) {
3496+
if (!mem) {
3497+
return false;
3498+
}
3499+
3500+
return mem->eagle3_recurrent_round_promote(live_seq_id, depth);
3501+
}
3502+
3503+
void llama_memory_eagle3_recurrent_clear(
3504+
llama_memory_t mem,
3505+
llama_seq_id live_seq_id) {
3506+
if (!mem) {
3507+
return;
3508+
}
3509+
3510+
mem->eagle3_recurrent_round_clear(live_seq_id);
3511+
}
3512+
34713513
// llama state API
34723514

34733515
// deprecated

src/llama-memory-hybrid.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,18 @@ std::map<ggml_backend_buffer_type_t, size_t> llama_memory_hybrid::memory_breakdo
176176
return mb;
177177
}
178178

179+
bool llama_memory_hybrid::eagle3_recurrent_round_begin(llama_seq_id live_seq_id, uint32_t n_depth, llama_pos p0) {
180+
return mem_recr->eagle3_recurrent_round_begin(live_seq_id, n_depth, p0);
181+
}
182+
183+
bool llama_memory_hybrid::eagle3_recurrent_round_promote(llama_seq_id live_seq_id, uint32_t depth) {
184+
return mem_recr->eagle3_recurrent_round_promote(live_seq_id, depth);
185+
}
186+
187+
void llama_memory_hybrid::eagle3_recurrent_round_clear(llama_seq_id live_seq_id) {
188+
mem_recr->eagle3_recurrent_round_clear(live_seq_id);
189+
}
190+
179191
void llama_memory_hybrid::state_write(llama_io_write_i & io, llama_seq_id seq_id, llama_state_seq_flags flags) const {
180192
if ((flags & LLAMA_STATE_SEQ_FLAGS_PARTIAL_ONLY) == 0) {
181193
mem_attn->state_write(io, seq_id, flags);

src/llama-memory-hybrid.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ class llama_memory_hybrid : public llama_memory_i {
7070

7171
std::map<ggml_backend_buffer_type_t, size_t> memory_breakdown() const override;
7272

73+
bool eagle3_recurrent_round_begin(
74+
llama_seq_id live_seq_id,
75+
uint32_t n_depth,
76+
llama_pos p0) override;
77+
78+
bool eagle3_recurrent_round_promote(
79+
llama_seq_id live_seq_id,
80+
uint32_t depth) override;
81+
82+
void eagle3_recurrent_round_clear(llama_seq_id live_seq_id) override;
83+
7384
// state write/load
7485

7586
void state_write(llama_io_write_i & io, llama_seq_id seq_id = -1, llama_state_seq_flags flags = 0) const override;

0 commit comments

Comments
 (0)