fix(cache): clear history on model scope changes (#1052)

This commit is contained in:
axobase001
2026-05-07 19:51:13 +08:00
committed by GitHub
parent 4642ff9a6d
commit 9fc0c416e4
4 changed files with 95 additions and 12 deletions
+50 -4
View File
@@ -99,14 +99,19 @@ pub fn model(app: &mut App, model_name: Option<&str>) -> CommandResult {
if let Some(name) = model_name {
if name.trim().eq_ignore_ascii_case("auto") {
let old_model = app.model_display_label();
let model_changed = !app.auto_model || app.model != "auto";
app.auto_model = true;
app.model = "auto".to_string();
app.last_effective_model = None;
app.reasoning_effort = ReasoningEffort::Auto;
app.last_effective_reasoning_effort = None;
app.update_model_compaction_budget();
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
if model_changed {
app.clear_model_scoped_telemetry();
} else {
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
}
return CommandResult::with_message_and_action(
tr(app.ui_locale, MessageId::ModelChanged)
.replace("{old}", &old_model)
@@ -121,12 +126,17 @@ pub fn model(app: &mut App, model_name: Option<&str>) -> CommandResult {
));
};
let old_model = app.model_display_label();
let model_changed = app.auto_model || app.model != model_id;
app.auto_model = false;
app.model = model_id.clone();
app.last_effective_model = None;
app.update_model_compaction_budget();
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
if model_changed {
app.clear_model_scoped_telemetry();
} else {
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
}
CommandResult::with_message_and_action(
tr(app.ui_locale, MessageId::ModelChanged)
.replace("{old}", &old_model)
@@ -485,6 +495,42 @@ mod tests {
assert_eq!(app.session.last_completion_tokens, None);
}
#[test]
fn model_switch_clears_turn_cache_history() {
let mut app = create_test_app();
app.push_turn_cache_record(TurnCacheRecord {
input_tokens: 100,
output_tokens: 25,
cache_hit_tokens: Some(70),
cache_miss_tokens: Some(30),
reasoning_replay_tokens: Some(12),
recorded_at: Instant::now(),
});
let result = model(&mut app, Some("deepseek-v4-flash"));
assert!(result.message.is_some());
assert!(app.session.turn_cache_history.is_empty());
}
#[test]
fn model_reset_same_model_keeps_turn_cache_history() {
let mut app = create_test_app();
app.push_turn_cache_record(TurnCacheRecord {
input_tokens: 100,
output_tokens: 25,
cache_hit_tokens: Some(70),
cache_miss_tokens: Some(30),
reasoning_replay_tokens: Some(12),
recorded_at: Instant::now(),
});
let result = model(&mut app, Some("deepseek-v4-pro"));
assert!(result.message.is_some());
assert_eq!(app.session.turn_cache_history.len(), 1);
}
#[test]
fn test_model_auto_enables_auto_thinking() {
let mut app = create_test_app();
+9
View File
@@ -1081,6 +1081,15 @@ impl App {
}
}
pub(crate) fn clear_model_scoped_telemetry(&mut self) {
self.session.last_prompt_tokens = None;
self.session.last_completion_tokens = None;
self.session.last_prompt_cache_hit_tokens = None;
self.session.last_prompt_cache_miss_tokens = None;
self.session.last_reasoning_replay_tokens = None;
self.session.turn_cache_history.clear();
}
pub fn tr(&self, id: MessageId) -> &'static str {
tr(self.ui_locale, id)
}
+8 -7
View File
@@ -3884,11 +3884,7 @@ async fn apply_model_picker_choice(
app.last_effective_model = None;
app.model = model.clone();
app.update_model_compaction_budget();
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
app.session.last_prompt_cache_hit_tokens = None;
app.session.last_prompt_cache_miss_tokens = None;
app.session.last_reasoning_replay_tokens = None;
app.clear_model_scoped_telemetry();
}
if effort_changed {
app.reasoning_effort = effort;
@@ -4006,11 +4002,16 @@ async fn switch_provider(
}
let new_model = config.default_model();
let cache_scope_changed = previous_provider != target || previous_model != new_model;
app.api_provider = target;
app.model = new_model.clone();
app.update_model_compaction_budget();
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
if cache_scope_changed {
app.clear_model_scoped_telemetry();
} else {
app.session.last_prompt_tokens = None;
app.session.last_completion_tokens = None;
}
let _ = engine_handle.send(Op::Shutdown).await;
let engine_config = build_engine_config(app, config);
+28 -1
View File
@@ -1,5 +1,5 @@
use super::*;
use crate::config::Config;
use crate::config::{ApiProvider, Config};
use crate::config_ui::{self, WebConfigSession, WebConfigSessionEvent};
use crate::core::engine::mock_engine_handle;
use crate::tui::file_mention::{
@@ -899,6 +899,33 @@ async fn model_change_update_syncs_engine_model_before_compaction() {
}
}
#[tokio::test]
async fn provider_switch_clears_turn_cache_history() {
let mut app = create_test_app();
app.push_turn_cache_record(crate::tui::app::TurnCacheRecord {
input_tokens: 100,
output_tokens: 25,
cache_hit_tokens: Some(70),
cache_miss_tokens: Some(30),
reasoning_replay_tokens: Some(12),
recorded_at: Instant::now(),
});
let mut engine = mock_engine_handle();
let mut config = Config::default();
switch_provider(
&mut app,
&mut engine.handle,
&mut config,
ApiProvider::Ollama,
None,
)
.await;
assert_eq!(app.api_provider, ApiProvider::Ollama);
assert!(app.session.turn_cache_history.is_empty());
}
#[tokio::test]
async fn dispatch_user_message_failed_send_clears_loading_state() {
let mut app = create_test_app();