fix(cache): clear history on model scope changes (#1052)
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user