fix(quick-1): bail on NULL KV cache and improve prefill error diagnostics
- engine.rs: new_session() now returns Err when llama_get_memory returns NULL instead of silently continuing, preventing decode corruption on 2nd request - session.rs: decode_prefill error includes token count for easier diagnosis
This commit is contained in:
1 parent
54a58ee9c7
commit
d9198795bf
2 files changed
+45
-27
No files matched your search
@@ -263,10 +263,6 @@ impl InferenceEngine {
|
|||||||
info!("Running engine warmup (dummy decode)…");
|
info!("Running engine warmup (dummy decode)…");
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Mark context as warmup so llama.cpp skips perf counters
|
|
||||||
if let Ok(f) = std::panic::catch_unwind(|| {}) {
|
|
||||||
let _ = f;
|
|
||||||
}
|
|
||||||
(self.lib.set_warmup)(self.ctx.ptr, true);
|
(self.lib.set_warmup)(self.ctx.ptr, true);
|
||||||
// set_warmup may not exist in all builds; call decode with 1 token
|
// set_warmup may not exist in all builds; call decode with 1 token
|
||||||
let dummy_token: llama_sys::llama_token = 1; // BOS-ish token
|
let dummy_token: llama_sys::llama_token = 1; // BOS-ish token
|
||||||
@@ -279,7 +275,11 @@ impl InferenceEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let memory = (self.lib.get_memory)(self.ctx.ptr);
|
let memory = (self.lib.get_memory)(self.ctx.ptr);
|
||||||
(self.lib.memory_clear)(memory, false);
|
if memory.is_null() {
|
||||||
|
warn!("llama_get_memory returned NULL during warmup — KV cache not cleared");
|
||||||
|
} else {
|
||||||
|
(self.lib.memory_clear)(memory, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("Warmup complete");
|
info!("Warmup complete");
|
||||||
@@ -295,6 +295,21 @@ impl InferenceEngine {
|
|||||||
prompt: &str,
|
prompt: &str,
|
||||||
sampler_cfg: SamplerConfig,
|
sampler_cfg: SamplerConfig,
|
||||||
) -> Result<GenerationSession> {
|
) -> Result<GenerationSession> {
|
||||||
|
// Clear KV cache from any previous session so the new prefill starts at position 0.
|
||||||
|
// `true` forces a full clear including M-RoPE position metadata; `false` only clears
|
||||||
|
// cell occupancy flags and leaves position data intact, which causes the
|
||||||
|
// "inconsistent sequence positions" error on models that use M-RoPE (e.g. Qwen).
|
||||||
|
unsafe {
|
||||||
|
let memory = (self.lib.get_memory)(self.ctx.ptr);
|
||||||
|
if memory.is_null() {
|
||||||
|
tracing::error!("llama_get_memory returned NULL — cannot clear KV cache, refusing to create session to avoid decode corruption");
|
||||||
|
anyhow::bail!("KV cache clear failed: llama_get_memory returned NULL");
|
||||||
|
} else {
|
||||||
|
(self.lib.memory_clear)(memory, true);
|
||||||
|
tracing::info!("engine.kv_cache_cleared");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let vocab = unsafe { (self.lib.model_get_vocab)(self._model.ptr) };
|
let vocab = unsafe { (self.lib.model_get_vocab)(self._model.ptr) };
|
||||||
GenerationSession::with_vocab(
|
GenerationSession::with_vocab(
|
||||||
Arc::clone(&self.lib),
|
Arc::clone(&self.lib),
|
||||||
|
|||||||
@@ -52,18 +52,6 @@ struct PrefillChunkPlan {
|
|||||||
unsafe impl Send for GenerationSession {}
|
unsafe impl Send for GenerationSession {}
|
||||||
|
|
||||||
impl GenerationSession {
|
impl GenerationSession {
|
||||||
/// Tokenize prompt and run prefill decode. Returns a session ready to generate.
|
|
||||||
pub fn new(
|
|
||||||
_lib: Arc<LlamaLib>,
|
|
||||||
_ctx: *mut llama_context,
|
|
||||||
_sampler_cfg: SamplerConfig,
|
|
||||||
) -> Result<Self> {
|
|
||||||
// Get vocab from context's model (via a workaround: we pass vocab separately)
|
|
||||||
// For now we'll get it indirectly — see Engine::new_session which passes vocab.
|
|
||||||
// This constructor signature will be refined; use the vocab variant below.
|
|
||||||
bail!("Use GenerationSession::with_vocab() instead")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Full constructor called by InferenceEngine::new_session().
|
/// Full constructor called by InferenceEngine::new_session().
|
||||||
pub fn with_vocab(
|
pub fn with_vocab(
|
||||||
lib: Arc<LlamaLib>,
|
lib: Arc<LlamaLib>,
|
||||||
@@ -159,8 +147,10 @@ impl GenerationSession {
|
|||||||
let prefill_ms = prefill_start.elapsed().as_secs_f64() * 1000.0;
|
let prefill_ms = prefill_start.elapsed().as_secs_f64() * 1000.0;
|
||||||
let prefill_tps = n_prompt_tokens as f64 / (prefill_ms / 1000.0);
|
let prefill_tps = n_prompt_tokens as f64 / (prefill_ms / 1000.0);
|
||||||
info!(
|
info!(
|
||||||
"Prefill finished in {:.2} ms ({:.2} t/s)",
|
n_prompt_tokens,
|
||||||
prefill_ms, prefill_tps
|
prefill_ms = format_args!("{:.2}", prefill_ms),
|
||||||
|
prefill_tps = format_args!("{:.2}", prefill_tps),
|
||||||
|
"session.prefill_complete"
|
||||||
);
|
);
|
||||||
|
|
||||||
// create session state
|
// create session state
|
||||||
@@ -224,6 +214,14 @@ impl GenerationSession {
|
|||||||
self.n_decoded += 1;
|
self.n_decoded += 1;
|
||||||
self.tokens_generated += 1;
|
self.tokens_generated += 1;
|
||||||
|
|
||||||
|
if self.tokens_generated % 100 == 0 {
|
||||||
|
tracing::debug!(
|
||||||
|
tokens_generated = self.tokens_generated,
|
||||||
|
max_tokens = self.max_tokens,
|
||||||
|
"session.generation_progress"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Detokenize
|
// Detokenize
|
||||||
let token_text = self.detokenize_token(new_token);
|
let token_text = self.detokenize_token(new_token);
|
||||||
|
|
||||||
@@ -249,14 +247,18 @@ impl GenerationSession {
|
|||||||
self.tokens_per_sec = self.tokens_generated as f64 / elapsed;
|
self.tokens_per_sec = self.tokens_generated as f64 / elapsed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let stop_reason = if self.tokens_generated >= self.max_tokens {
|
||||||
|
"max_tokens"
|
||||||
|
} else {
|
||||||
|
"eos_or_stop_sequence"
|
||||||
|
};
|
||||||
info!(
|
info!(
|
||||||
tokens_generated = self.tokens_generated,
|
tokens_generated = self.tokens_generated,
|
||||||
tps = format!("{:.1}", self.tokens_per_sec),
|
max_tokens = self.max_tokens,
|
||||||
prefill_ms = self
|
tps = self.tokens_per_sec,
|
||||||
.prefill_ms
|
prefill_ms = self.prefill_ms.unwrap_or(0.0),
|
||||||
.map(|v| format!("{:.1}", v))
|
stop_reason,
|
||||||
.unwrap_or_default(),
|
"session.generation_complete"
|
||||||
"Generation complete"
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -347,9 +349,10 @@ fn decode_prefill(
|
|||||||
|
|
||||||
if ret != 0 {
|
if ret != 0 {
|
||||||
bail!(
|
bail!(
|
||||||
"llama_decode (prefill) returned {} while decoding prompt chunk {:?}",
|
"llama_decode (prefill) returned {} for chunk {:?} — likely KV cache not cleared or context window ({} tokens) exceeded by prompt",
|
||||||
ret,
|
ret,
|
||||||
plan.range
|
plan.range,
|
||||||
|
tokens.len()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user