Skip to content

Commit e85be4d

Browse files
authored
Merge pull request #92 from wsp1911/fix/mode-switch-not-effective
fix: mode switch not work
2 parents 3c1a12c + cab46ba commit e85be4d

3 files changed

Lines changed: 125 additions & 88 deletions

File tree

src/crates/core/src/agentic/coordination/coordinator.rs

Lines changed: 61 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ use crate::agentic::events::{
1111
AgenticEvent, EventPriority, EventQueue, EventRouter, EventSubscriber,
1212
};
1313
use crate::agentic::execution::{ExecutionContext, ExecutionEngine};
14+
use crate::agentic::image_analysis::ImageContextData;
1415
use crate::agentic::session::SessionManager;
1516
use crate::agentic::tools::pipeline::{SubagentParentInfo, ToolPipeline};
16-
use crate::agentic::image_analysis::ImageContextData;
1717
use crate::util::errors::{BitFunError, BitFunResult};
1818
use log::{debug, error, info, warn};
1919
use std::sync::Arc;
@@ -147,8 +147,7 @@ impl ConversationCoordinator {
147147
workspace_path: Option<String>,
148148
) -> BitFunResult<Session> {
149149
let effective_workspace_path = workspace_path.or_else(|| {
150-
crate::infrastructure::get_workspace_path()
151-
.map(|p| p.to_string_lossy().to_string())
150+
crate::infrastructure::get_workspace_path().map(|p| p.to_string_lossy().to_string())
152151
});
153152

154153
// Persist the workspace binding inside the session config so execution can
@@ -246,17 +245,16 @@ impl ConversationCoordinator {
246245
terminal_session_id: existing
247246
.as_ref()
248247
.and_then(|m| m.terminal_session_id.clone()),
249-
snapshot_session_id: session
250-
.snapshot_session_id
251-
.clone()
252-
.or_else(|| existing.as_ref().and_then(|m| m.snapshot_session_id.clone())),
248+
snapshot_session_id: session.snapshot_session_id.clone().or_else(|| {
249+
existing
250+
.as_ref()
251+
.and_then(|m| m.snapshot_session_id.clone())
252+
}),
253253
tags: existing
254254
.as_ref()
255255
.map(|m| m.tags.clone())
256256
.unwrap_or_default(),
257-
custom_metadata: existing
258-
.as_ref()
259-
.and_then(|m| m.custom_metadata.clone()),
257+
custom_metadata: existing.as_ref().and_then(|m| m.custom_metadata.clone()),
260258
todos: existing.as_ref().and_then(|m| m.todos.clone()),
261259
workspace_path: Some(workspace_path),
262260
};
@@ -390,8 +388,15 @@ impl ConversationCoordinator {
390388
agent_type: String,
391389
trigger_source: DialogTriggerSource,
392390
) -> BitFunResult<()> {
393-
self.start_dialog_turn_internal(session_id, user_input, None, turn_id, agent_type, trigger_source)
394-
.await
391+
self.start_dialog_turn_internal(
392+
session_id,
393+
user_input,
394+
None,
395+
turn_id,
396+
agent_type,
397+
trigger_source,
398+
)
399+
.await
395400
}
396401

397402
pub async fn start_dialog_turn_with_image_contexts(
@@ -560,12 +565,40 @@ impl ConversationCoordinator {
560565
}
561566
};
562567

563-
let effective_agent_type = if session.agent_type.is_empty() {
564-
agent_type
565-
} else {
568+
let requested_agent_type = agent_type.trim().to_string();
569+
570+
let effective_agent_type = if !requested_agent_type.is_empty() {
571+
requested_agent_type.clone()
572+
} else if !session.agent_type.is_empty() {
566573
session.agent_type.clone()
574+
} else {
575+
"agentic".to_string()
567576
};
568577

578+
debug!(
579+
"Resolved dialog turn agent type: session_id={}, turn_id={}, requested_agent_type={}, session_agent_type={}, effective_agent_type={}, trigger_source={:?}",
580+
session_id,
581+
turn_id.as_deref().unwrap_or(""),
582+
if requested_agent_type.is_empty() {
583+
"<empty>"
584+
} else {
585+
requested_agent_type.as_str()
586+
},
587+
if session.agent_type.is_empty() {
588+
"<empty>"
589+
} else {
590+
session.agent_type.as_str()
591+
},
592+
effective_agent_type,
593+
trigger_source
594+
);
595+
596+
if session.agent_type != effective_agent_type {
597+
self.session_manager
598+
.update_session_agent_type(&session_id, &effective_agent_type)
599+
.await?;
600+
}
601+
569602
debug!(
570603
"Checking session state: session_id={}, state={:?}",
571604
session_id, session.state
@@ -709,8 +742,14 @@ impl ConversationCoordinator {
709742
// vision model to pre-analyze them, then enhance the user message with text descriptions.
710743
// This is the single authoritative code path for all image handling (desktop, remote, bot).
711744
// If no vision model is configured, the request is rejected with a user-friendly message.
712-
let (user_input, image_contexts) =
713-
self.pre_analyze_images_if_needed(user_input, image_contexts, &session_id, user_message_metadata.clone()).await?;
745+
let (user_input, image_contexts) = self
746+
.pre_analyze_images_if_needed(
747+
user_input,
748+
image_contexts,
749+
&session_id,
750+
user_message_metadata.clone(),
751+
)
752+
.await?;
714753

715754
let wrapped_user_input = self
716755
.wrap_user_input(&effective_agent_type, user_input)
@@ -906,7 +945,10 @@ impl ConversationCoordinator {
906945
let is_cancellation = matches!(&e, BitFunError::Cancelled(_));
907946

908947
if is_cancellation {
909-
info!("Dialog turn cancelled: session={}, turn={}", session_id_clone, turn_id_clone);
948+
info!(
949+
"Dialog turn cancelled: session={}, turn={}",
950+
session_id_clone, turn_id_clone
951+
);
910952

911953
// The execution engine only emits DialogTurnCancelled when
912954
// cancellation is detected between rounds. If cancellation
@@ -968,11 +1010,7 @@ impl ConversationCoordinator {
9681010
.await;
9691011

9701012
let _ = session_manager
971-
.fail_dialog_turn(
972-
&session_id_clone,
973-
&turn_id_clone,
974-
e.to_string(),
975-
)
1013+
.fail_dialog_turn(&session_id_clone, &turn_id_clone, e.to_string())
9761014
.await;
9771015

9781016
let _ = session_manager

src/crates/core/src/agentic/coordination/scheduler.rs

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -107,29 +107,16 @@ impl DialogScheduler {
107107
.map(|s| s.state.clone());
108108

109109
match state {
110-
None => {
111-
self.coordinator
112-
.start_dialog_turn(
113-
session_id,
114-
user_input,
115-
turn_id,
116-
agent_type,
117-
trigger_source,
118-
)
119-
.await
120-
.map_err(|e| e.to_string())
121-
}
110+
None => self
111+
.coordinator
112+
.start_dialog_turn(session_id, user_input, turn_id, agent_type, trigger_source)
113+
.await
114+
.map_err(|e| e.to_string()),
122115

123116
Some(SessionState::Error { .. }) => {
124117
self.clear_queue_and_debounce(&session_id);
125118
self.coordinator
126-
.start_dialog_turn(
127-
session_id,
128-
user_input,
129-
turn_id,
130-
agent_type,
131-
trigger_source,
132-
)
119+
.start_dialog_turn(session_id, user_input, turn_id, agent_type, trigger_source)
133120
.await
134121
.map_err(|e| e.to_string())
135122
}
@@ -143,13 +130,7 @@ impl DialogScheduler {
143130
.unwrap_or(false);
144131

145132
if in_debounce || queue_non_empty {
146-
self.enqueue(
147-
&session_id,
148-
user_input,
149-
turn_id,
150-
agent_type,
151-
trigger_source,
152-
)?;
133+
self.enqueue(&session_id, user_input, turn_id, agent_type, trigger_source)?;
153134
self.schedule_debounce(session_id);
154135
Ok(())
155136
} else {
@@ -167,13 +148,7 @@ impl DialogScheduler {
167148
}
168149

169150
Some(SessionState::Processing { .. }) => {
170-
self.enqueue(
171-
&session_id,
172-
user_input,
173-
turn_id,
174-
agent_type,
175-
trigger_source,
176-
)?;
151+
self.enqueue(&session_id, user_input, turn_id, agent_type, trigger_source)?;
177152
Ok(())
178153
}
179154
}
@@ -277,8 +252,7 @@ impl DialogScheduler {
277252
session_id_clone
278253
);
279254

280-
let (merged_input, turn_id, agent_type, trigger_source) =
281-
merge_messages(messages);
255+
let (merged_input, turn_id, agent_type, trigger_source) = merge_messages(messages);
282256

283257
if let Err(e) = coordinator
284258
.start_dialog_turn(
@@ -349,19 +323,22 @@ impl DialogScheduler {
349323
/// Queued #2
350324
/// <second message>
351325
/// ```
352-
fn merge_messages(messages: Vec<QueuedTurn>) -> (String, Option<String>, String, DialogTriggerSource) {
326+
fn merge_messages(
327+
messages: Vec<QueuedTurn>,
328+
) -> (String, Option<String>, String, DialogTriggerSource) {
353329
if messages.len() == 1 {
354330
let m = messages.into_iter().next().unwrap();
355-
return (
356-
m.user_input,
357-
m.turn_id,
358-
m.agent_type,
359-
m.trigger_source,
360-
);
331+
return (m.user_input, m.turn_id, m.agent_type, m.trigger_source);
361332
}
362333

363-
let agent_type = messages[0].agent_type.clone();
364-
let trigger_source = messages[0].trigger_source;
334+
let agent_type = messages
335+
.last()
336+
.map(|m| m.agent_type.clone())
337+
.unwrap_or_else(|| "agentic".to_string());
338+
let trigger_source = messages
339+
.last()
340+
.map(|m| m.trigger_source)
341+
.unwrap_or(DialogTriggerSource::DesktopUi);
365342

366343
let entries: Vec<String> = messages
367344
.iter()

src/crates/core/src/agentic/session/session_manager.rs

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,7 @@ impl SessionManager {
175175
}
176176

177177
/// Update session title (in-memory + persistence)
178-
pub async fn update_session_title(
179-
&self,
180-
session_id: &str,
181-
title: &str,
182-
) -> BitFunResult<()> {
178+
pub async fn update_session_title(&self, session_id: &str, title: &str) -> BitFunResult<()> {
183179
let workspace_path = self
184180
.sessions
185181
.get(session_id)
@@ -206,9 +202,7 @@ impl SessionManager {
206202
.await
207203
{
208204
Ok(conv_mgr) => {
209-
if let Ok(Some(mut meta)) =
210-
conv_mgr.load_session_metadata(session_id).await
211-
{
205+
if let Ok(Some(mut meta)) = conv_mgr.load_session_metadata(session_id).await {
212206
meta.session_name = title.to_string();
213207
meta.touch();
214208
if let Err(e) = conv_mgr.save_session_metadata(&meta).await {
@@ -233,6 +227,37 @@ impl SessionManager {
233227
Ok(())
234228
}
235229

230+
/// Update session agent type (in-memory + persistence)
231+
pub async fn update_session_agent_type(
232+
&self,
233+
session_id: &str,
234+
agent_type: &str,
235+
) -> BitFunResult<()> {
236+
if let Some(mut session) = self.sessions.get_mut(session_id) {
237+
session.agent_type = agent_type.to_string();
238+
session.updated_at = SystemTime::now();
239+
session.last_activity_at = SystemTime::now();
240+
} else {
241+
return Err(BitFunError::NotFound(format!(
242+
"Session not found: {}",
243+
session_id
244+
)));
245+
}
246+
247+
if self.config.enable_persistence {
248+
if let Some(session) = self.sessions.get(session_id) {
249+
self.persistence_manager.save_session(&session).await?;
250+
}
251+
}
252+
253+
debug!(
254+
"Session agent type updated: session_id={}, agent_type={}",
255+
session_id, agent_type
256+
);
257+
258+
Ok(())
259+
}
260+
236261
/// Update session activity time
237262
pub fn touch_session(&self, session_id: &str) {
238263
if let Some(mut session) = self.sessions.get_mut(session_id) {
@@ -552,13 +577,12 @@ impl SessionManager {
552577
}
553578

554579
// 2. Add user message to history and compression managers
555-
let user_message = if let Some(images) =
556-
image_contexts.as_ref().filter(|v| !v.is_empty()).cloned()
557-
{
558-
Message::user_multimodal(user_input, images).with_turn_id(turn_id.clone())
559-
} else {
560-
Message::user(user_input).with_turn_id(turn_id.clone())
561-
};
580+
let user_message =
581+
if let Some(images) = image_contexts.as_ref().filter(|v| !v.is_empty()).cloned() {
582+
Message::user_multimodal(user_input, images).with_turn_id(turn_id.clone())
583+
} else {
584+
Message::user(user_input).with_turn_id(turn_id.clone())
585+
};
562586
self.history_manager
563587
.add_message(session_id, user_message.clone())
564588
.await?;
@@ -662,11 +686,7 @@ impl SessionManager {
662686
Ok(context_messages) => {
663687
if let Err(err) = self
664688
.persistence_manager
665-
.save_turn_context_snapshot(
666-
session_id,
667-
turn.turn_index,
668-
&context_messages,
669-
)
689+
.save_turn_context_snapshot(session_id, turn.turn_index, &context_messages)
670690
.await
671691
{
672692
warn!(
@@ -707,7 +727,9 @@ impl SessionManager {
707727
limit: usize,
708728
before_message_id: Option<&str>,
709729
) -> BitFunResult<(Vec<Message>, bool)> {
710-
self.history_manager.get_messages_paginated(session_id, limit, before_message_id).await
730+
self.history_manager
731+
.get_messages_paginated(session_id, limit, before_message_id)
732+
.await
711733
}
712734

713735
/// Get session's context messages (may be compressed)

0 commit comments

Comments
 (0)