feat: more native menus with expected reactions

This commit is contained in:
Georg Bauer
2026-07-27 15:09:12 +02:00
parent 8f02f0934b
commit c96675c462
11 changed files with 738 additions and 70 deletions

View File

@@ -467,6 +467,19 @@ pub(crate) struct GenerationOutput {
pub(crate) checkpoint_bytes: u64,
}
struct CheckpointSelection {
found: bool,
incompatible: bool,
}
fn checkpoint_rebuild_activity(incompatible: bool) -> &'static str {
if incompatible {
"Rebuilding context: the checkpoint belongs to a different model or model configuration."
} else {
"Rebuilding context: the saved history or generation settings changed."
}
}
#[cfg(target_os = "macos")]
pub(crate) struct CompactionOutput {
pub(crate) summary: String,
@@ -543,6 +556,7 @@ impl Generator {
self.executor.model().summary()
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn generate(
&mut self,
checkpoint: &Path,
@@ -551,14 +565,19 @@ impl Generator {
cancelled: &AtomicBool,
mut emit: impl FnMut(bool, String),
mut progress: impl FnMut(u32, u32, Option<f32>),
mut phase: impl FnMut(&'static str),
) -> Result<GenerationOutput, String> {
let history = messages
.split_last()
.map_or(messages, |(_, history)| history);
self.select_checkpoint(
let checkpoint_present = checkpoint.is_file();
let selected = self.select_checkpoint(
checkpoint,
conversation_tag(&settings.system_prompt, settings.reasoning_mode, history),
)?;
if checkpoint_present && !selected.found {
phase(checkpoint_rebuild_activity(selected.incompatible));
}
let result = self.generate_inner(messages, settings, cancelled, &mut emit, &mut progress);
self.publish_execution_stats();
let (mut output, prompt_complete) = result?;
@@ -632,7 +651,7 @@ impl Generator {
let mut previous_checkpoint = self.checkpoint.clone();
if self.executor.checkpoint_tag() != history_tag {
if let Some(entry) = store.find(&history_key, self.executor.context()) {
if self.select_checkpoint(&entry.checkpoint, entry.tag)? {
if self.select_checkpoint(&entry.checkpoint, entry.tag)?.found {
store.touch(&entry)?;
self.last_store_tokens = entry.tokens;
previous_checkpoint = Some(entry.checkpoint);
@@ -861,19 +880,25 @@ impl Generator {
&mut self,
checkpoint: &Path,
expected_tag: [u8; 32],
) -> Result<bool, String> {
) -> Result<CheckpointSelection, String> {
let resident_hit = self.activate_resident(checkpoint.to_owned())?;
if resident_hit && self.executor.checkpoint_tag() == expected_tag {
self.checkpoint = Some(checkpoint.to_owned());
self.metrics.kv_lookup(KvLookup::MemoryHit);
return Ok(true);
return Ok(CheckpointSelection {
found: true,
incompatible: false,
});
}
if self.checkpoint.as_deref() == Some(checkpoint) {
if !checkpoint.is_file() {
self.executor.reset()?;
self.checkpoint = None;
self.metrics.kv_lookup(KvLookup::Miss);
return Ok(false);
return Ok(CheckpointSelection {
found: false,
incompatible: false,
});
}
let found = self.executor.checkpoint_tag() == expected_tag;
self.metrics.kv_lookup(if found {
@@ -881,7 +906,10 @@ impl Generator {
} else {
KvLookup::Miss
});
return Ok(found);
return Ok(CheckpointSelection {
found,
incompatible: false,
});
}
self.executor.reset()?;
self.metrics.kv_read_started();
@@ -892,6 +920,7 @@ impl Generator {
self.metrics
.kv_read_finished(started.elapsed(), loaded.is_err());
let found = matches!(loaded, Ok(true)) && self.executor.checkpoint_tag() == expected_tag;
let incompatible = loaded.is_err();
let lookup = match loaded {
Ok(true) if found => KvLookup::DiskHit,
Ok(true) | Ok(false) => KvLookup::Miss,
@@ -903,7 +932,10 @@ impl Generator {
};
self.metrics.kv_lookup(lookup);
self.checkpoint = Some(checkpoint.to_owned());
Ok(found)
Ok(CheckpointSelection {
found,
incompatible,
})
}
fn activate_resident(&mut self, key: PathBuf) -> Result<bool, String> {
@@ -1616,6 +1648,12 @@ mod sampling_tests {
assert!(conversation_key("System", ReasoningMode::High, &messages).starts_with(&prefix));
}
#[test]
fn checkpoint_rebuilds_explain_compatibility_and_history_misses() {
assert!(checkpoint_rebuild_activity(true).contains("different model"));
assert!(checkpoint_rebuild_activity(false).contains("history"));
}
#[test]
#[ignore = "requires the 80 GiB Flash checkpoint and Apple Metal"]
fn metal_executes_real_flash_token() {