diff --git a/crates/tui/src/core/engine.rs b/crates/tui/src/core/engine.rs index 2f920b3f..e5e4c612 100644 --- a/crates/tui/src/core/engine.rs +++ b/crates/tui/src/core/engine.rs @@ -789,10 +789,12 @@ impl Engine { .send(Event::status(format!("Mode changed to: {mode:?}"))) .await; } - Op::SetModel { model } => { + Op::SetModel { model, mode } => { self.session.auto_model = model.trim().eq_ignore_ascii_case("auto"); self.session.model = model; self.config.model.clone_from(&self.session.model); + self.refresh_system_prompt(mode); + self.emit_session_updated().await; let _ = self .tx_event .send(Event::status(format!( diff --git a/crates/tui/src/core/engine/tests.rs b/crates/tui/src/core/engine/tests.rs index 783f3128..d12a2af4 100644 --- a/crates/tui/src/core/engine/tests.rs +++ b/crates/tui/src/core/engine/tests.rs @@ -1238,6 +1238,61 @@ async fn session_update_preserves_reasoning_tool_only_turn() { assert_eq!(messages, vec![assistant]); } +#[tokio::test] +async fn set_model_reloads_instruction_sources_and_updates_session_prompt() { + let tmp = tempdir().expect("tempdir"); + let instructions = tmp.path().join("instructions.md"); + fs::write(&instructions, "FLASH_INSTRUCTIONS_MARKER").expect("write instructions"); + let config = EngineConfig { + workspace: tmp.path().to_path_buf(), + model: "deepseek-v4-flash".to_string(), + instructions: vec![instructions.clone().into()], + ..Default::default() + }; + let (engine, handle) = Engine::new(config, &Config::default()); + fs::write(&instructions, "PRO_INSTRUCTIONS_MARKER").expect("rewrite instructions"); + + let run = tokio::spawn(engine.run()); + handle + .send(Op::SetModel { + model: "deepseek-v4-pro".to_string(), + mode: AppMode::Agent, + }) + .await + .expect("send set model"); + + let (model, prompt) = { + let mut rx = handle.rx_event.write().await; + loop { + let event = tokio::time::timeout(std::time::Duration::from_secs(1), rx.recv()) + .await + .expect("session update after model switch") + .expect("event"); + if let Event::SessionUpdated { + model, + system_prompt, + .. + } = event + { + let prompt = match system_prompt.expect("system prompt") { + SystemPrompt::Text(text) => text, + SystemPrompt::Blocks(blocks) => blocks + .into_iter() + .map(|block| block.text) + .collect::>() + .join("\n"), + }; + break (model, prompt); + } + } + }; + run.abort(); + + assert_eq!(model, "deepseek-v4-pro"); + assert!(prompt.contains("PRO_INSTRUCTIONS_MARKER")); + assert!(!prompt.contains("FLASH_INSTRUCTIONS_MARKER")); +} + #[test] fn detects_context_length_errors_from_provider_payloads() { let msg = r#"SSE stream request failed: HTTP 400 Bad Request: {"error":{"message":"This model's maximum context length is 131072 tokens. However, you requested 153056 tokens (148960 in the messages, 4096 in the completion).","type":"invalid_request_error"}}"#; diff --git a/crates/tui/src/core/ops.rs b/crates/tui/src/core/ops.rs index df6f0aa0..ab61659e 100644 --- a/crates/tui/src/core/ops.rs +++ b/crates/tui/src/core/ops.rs @@ -63,9 +63,9 @@ pub enum Op { #[allow(dead_code)] ChangeMode { mode: AppMode }, - /// Update the model being used + /// Update the model being used and refresh the prompt for the current mode. #[allow(dead_code)] - SetModel { model: String }, + SetModel { model: String, mode: AppMode }, /// Update auto-compaction settings SetCompaction { config: CompactionConfig }, diff --git a/crates/tui/src/tui/ui.rs b/crates/tui/src/tui/ui.rs index 7d08f9e9..50c4b0b0 100644 --- a/crates/tui/src/tui/ui.rs +++ b/crates/tui/src/tui/ui.rs @@ -3437,6 +3437,7 @@ async fn run_event_loop( let _ = engine_handle .send(Op::SetModel { model: app.model.clone(), + mode: app.mode, }) .await; } @@ -4748,10 +4749,12 @@ async fn dispatch_user_message( async fn apply_model_and_compaction_update( engine_handle: &EngineHandle, compaction: crate::compaction::CompactionConfig, + mode: AppMode, ) { let _ = engine_handle .send(Op::SetModel { model: compaction.model.clone(), + mode, }) .await; let _ = engine_handle @@ -4779,6 +4782,7 @@ async fn drain_web_config_events( apply_model_and_compaction_update( engine_handle, app.compaction_config(), + app.mode, ) .await; } @@ -4803,6 +4807,7 @@ async fn drain_web_config_events( apply_model_and_compaction_update( engine_handle, app.compaction_config(), + app.mode, ) .await; } @@ -4888,7 +4893,7 @@ async fn apply_model_picker_choice( } if model_changed { - apply_model_and_compaction_update(engine_handle, app.compaction_config()).await; + apply_model_and_compaction_update(engine_handle, app.compaction_config(), app.mode).await; } let model_summary = if model_is_auto { @@ -5237,7 +5242,7 @@ async fn apply_command_result( } } AppAction::UpdateCompaction(compaction) => { - apply_model_and_compaction_update(engine_handle, compaction).await; + apply_model_and_compaction_update(engine_handle, compaction, app.mode).await; } AppAction::OpenConfigEditor(mode) => match mode { ConfigUiMode::Native => { @@ -5267,6 +5272,7 @@ async fn apply_command_result( apply_model_and_compaction_update( engine_handle, app.compaction_config(), + app.mode, ) .await; } @@ -6743,7 +6749,8 @@ async fn handle_view_events( if let Some(action) = result.action { match action { AppAction::UpdateCompaction(compaction) => { - apply_model_and_compaction_update(engine_handle, compaction).await; + apply_model_and_compaction_update(engine_handle, compaction, app.mode) + .await; } AppAction::OpenConfigView => {} _ => {} diff --git a/crates/tui/src/tui/ui/tests.rs b/crates/tui/src/tui/ui/tests.rs index ffc4e525..a03c9258 100644 --- a/crates/tui/src/tui/ui/tests.rs +++ b/crates/tui/src/tui/ui/tests.rs @@ -2021,11 +2021,12 @@ async fn model_change_update_syncs_engine_model_before_compaction() { let compaction = app.compaction_config(); let mut engine = crate::core::engine::mock_engine_handle(); - apply_model_and_compaction_update(&engine.handle, compaction).await; + apply_model_and_compaction_update(&engine.handle, compaction, app.mode).await; match engine.rx_op.recv().await.expect("set model op") { - crate::core::ops::Op::SetModel { model } => { + crate::core::ops::Op::SetModel { model, mode } => { assert_eq!(model, "deepseek-v4-flash"); + assert_eq!(mode, app.mode); } other => panic!("expected SetModel, got {other:?}"), }