Skip to content

Commit e480f4a

Browse files
fix: show command descriptions for auto-invoke tools (#725)
1 parent aee0cb0 commit e480f4a

File tree

2 files changed

+39
-109
lines changed

2 files changed

+39
-109
lines changed

crates/q_cli/src/cli/chat/conversation_state.rs

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -303,38 +303,6 @@ impl ConversationState {
303303
self.next_message = Some(msg);
304304
}
305305

306-
/// Sets the next user message with "interrupted" tool results.
307-
pub fn interrupt_tool_use(&mut self, interrupted_tools: Vec<(String, super::tools::Tool)>, deny_input: String) {
308-
debug_assert!(self.next_message.is_none());
309-
let tool_results = interrupted_tools
310-
.into_iter()
311-
.map(|(tool_use_id, _)| ToolResult {
312-
tool_use_id,
313-
content: vec![ToolResultContentBlock::Text(
314-
"Tool use was interrupted by the user".to_string(),
315-
)],
316-
status: fig_api_client::model::ToolResultStatus::Error,
317-
})
318-
.collect::<Vec<_>>();
319-
let user_input_message_context = UserInputMessageContext {
320-
shell_state: None,
321-
env_state: Some(build_env_state()),
322-
tool_results: Some(tool_results),
323-
tools: if self.tools.is_empty() {
324-
None
325-
} else {
326-
Some(self.tools.clone())
327-
},
328-
..Default::default()
329-
};
330-
let msg = UserInputMessage {
331-
content: deny_input,
332-
user_input_message_context: Some(user_input_message_context),
333-
user_intent: None,
334-
};
335-
self.next_message = Some(msg);
336-
}
337-
338306
/// Returns a [FigConversationState] capable of being sent by
339307
/// [fig_api_client::StreamingClient] while preparing the current conversation state to be sent
340308
/// in the next message.

crates/q_cli/src/cli/chat/mod.rs

Lines changed: 39 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -253,15 +253,11 @@ enum ChatState {
253253
PromptUser {
254254
/// Tool uses to present to the user.
255255
tool_uses: Option<Vec<QueuedTool>>,
256-
/// Whether or not the associated tool uses were previously interrupted.
257-
tools_were_interrupted: bool,
258256
},
259257
/// Handle the user input, depending on if any tools require execution.
260258
HandleInput {
261259
input: String,
262260
tool_uses: Option<Vec<QueuedTool>>,
263-
/// Whether or not the associated tool uses were previously interrupted.
264-
tools_were_interrupted: bool,
265261
},
266262
/// Validate the list of tool uses provided by the model.
267263
ValidateTools(Vec<ToolUse>),
@@ -275,10 +271,7 @@ enum ChatState {
275271

276272
impl Default for ChatState {
277273
fn default() -> Self {
278-
Self::PromptUser {
279-
tool_uses: None,
280-
tools_were_interrupted: false,
281-
}
274+
Self::PromptUser { tool_uses: None }
282275
}
283276
}
284277

@@ -293,10 +286,7 @@ where
293286

294287
let mut ctrl_c_stream = signal(SignalKind::interrupt())?;
295288

296-
let mut next_state = Some(ChatState::PromptUser {
297-
tool_uses: None,
298-
tools_were_interrupted: false,
299-
});
289+
let mut next_state = Some(ChatState::PromptUser { tool_uses: None });
300290

301291
if let Some(user_input) = self.initial_input.take() {
302292
execute!(
@@ -310,7 +300,6 @@ where
310300
next_state = Some(ChatState::HandleInput {
311301
input: user_input,
312302
tool_uses: None,
313-
tools_were_interrupted: false,
314303
});
315304
}
316305

@@ -320,15 +309,8 @@ where
320309
debug!(?chat_state, "changing to state");
321310

322311
let result = match chat_state {
323-
ChatState::PromptUser {
324-
tool_uses,
325-
tools_were_interrupted,
326-
} => self.prompt_user(tool_uses, tools_were_interrupted).await,
327-
ChatState::HandleInput {
328-
input,
329-
tool_uses,
330-
tools_were_interrupted,
331-
} => self.handle_input(input, tool_uses, tools_were_interrupted).await,
312+
ChatState::PromptUser { tool_uses } => self.prompt_user(tool_uses).await,
313+
ChatState::HandleInput { input, tool_uses } => self.handle_input(input, tool_uses).await,
332314
ChatState::ExecuteTools(tool_uses) => {
333315
let tool_uses_clone = tool_uses.clone();
334316
tokio::select! {
@@ -424,42 +406,20 @@ where
424406
},
425407
}
426408
self.conversation_state.fix_history();
427-
next_state = Some(ChatState::PromptUser {
428-
tool_uses: None,
429-
tools_were_interrupted: false,
430-
});
409+
next_state = Some(ChatState::PromptUser { tool_uses: None });
431410
},
432411
}
433412
}
434413
}
435414

436415
/// Read input from the user.
437-
async fn prompt_user(
438-
&mut self,
439-
mut tool_uses: Option<Vec<QueuedTool>>,
440-
tools_were_interrupted: bool,
441-
) -> Result<ChatState, ChatError> {
416+
async fn prompt_user(&mut self, mut tool_uses: Option<Vec<QueuedTool>>) -> Result<ChatState, ChatError> {
442417
if self.interactive {
443418
execute!(self.output, cursor::Show)?;
444419
}
445420
let tool_uses = tool_uses.take().unwrap_or_default();
446-
// Don't print the tools if they were previously interrupted.
447-
if !tool_uses.is_empty() && !tools_were_interrupted {
448-
let terminal_width = self.terminal_width();
449-
for (i, (_, tool)) in tool_uses.iter().enumerate() {
450-
queue!(
451-
self.output,
452-
style::SetForegroundColor(Color::Cyan),
453-
style::Print(format!("{}. {}\n", i + 1, tool.display_name())),
454-
style::SetForegroundColor(Color::Reset),
455-
style::SetForegroundColor(Color::DarkGrey),
456-
style::Print(format!("{}\n", "▔".repeat(terminal_width))),
457-
style::SetForegroundColor(Color::Reset),
458-
)?;
459-
tool.queue_description(&self.ctx, &mut self.output)
460-
.map_err(|e| ChatError::Custom(format!("failed to print tool: {}", e).into()))?;
461-
queue!(self.output, style::Print("\n"))?;
462-
}
421+
if !tool_uses.is_empty() {
422+
self.print_tool_descriptions(&tool_uses)?;
463423

464424
execute!(
465425
self.output,
@@ -485,21 +445,16 @@ where
485445
Ok(ChatState::HandleInput {
486446
input: user_input,
487447
tool_uses: Some(tool_uses),
488-
tools_were_interrupted,
489448
})
490449
}
491450

492451
async fn handle_input(
493452
&mut self,
494453
user_input: String,
495454
tool_uses: Option<Vec<QueuedTool>>,
496-
tools_were_interrupted: bool,
497455
) -> Result<ChatState, ChatError> {
498456
let Ok(command) = Command::parse(&user_input) else {
499-
return Ok(ChatState::PromptUser {
500-
tool_uses,
501-
tools_were_interrupted,
502-
});
457+
return Ok(ChatState::PromptUser { tool_uses });
503458
};
504459

505460
let tool_uses = tool_uses.unwrap_or_default();
@@ -518,10 +473,10 @@ where
518473
self.spinner = Some(Spinner::new(Spinners::Dots, "Thinking...".to_owned()));
519474
}
520475

521-
match (tool_uses.is_empty(), tools_were_interrupted) {
522-
(false, false) => self.conversation_state.abandon_tool_use(tool_uses, user_input),
523-
(false, true) => self.conversation_state.interrupt_tool_use(tool_uses, user_input),
524-
_ => self.conversation_state.append_new_user_message(user_input),
476+
if tool_uses.is_empty() {
477+
self.conversation_state.append_new_user_message(user_input);
478+
} else {
479+
self.conversation_state.abandon_tool_use(tool_uses, user_input);
525480
}
526481

527482
self.send_tool_use_telemetry().await;
@@ -536,10 +491,7 @@ where
536491
queue!(self.output, style::Print('\n'))?;
537492
std::process::Command::new("bash").args(["-c", &command]).status().ok();
538493
queue!(self.output, style::Print('\n'))?;
539-
ChatState::PromptUser {
540-
tool_uses: None,
541-
tools_were_interrupted,
542-
}
494+
ChatState::PromptUser { tool_uses: None }
543495
},
544496
Command::Clear => {
545497
self.conversation_state.clear();
@@ -551,17 +503,11 @@ where
551503
style::SetForegroundColor(Color::Reset)
552504
)?;
553505

554-
ChatState::PromptUser {
555-
tool_uses: None,
556-
tools_were_interrupted,
557-
}
506+
ChatState::PromptUser { tool_uses: None }
558507
},
559508
Command::Help => {
560509
execute!(self.output, style::Print(HELP_TEXT))?;
561-
ChatState::PromptUser {
562-
tool_uses: None,
563-
tools_were_interrupted,
564-
}
510+
ChatState::PromptUser { tool_uses: None }
565511
},
566512
Command::Quit => ChatState::Exit,
567513
})
@@ -571,15 +517,15 @@ where
571517
// Execute the requested tools.
572518
let terminal_width = self.terminal_width();
573519
let mut tool_results = vec![];
574-
for (i, tool) in tool_uses.into_iter().enumerate() {
520+
for tool in tool_uses {
575521
let mut tool_telemetry = self.tool_use_telemetry_events.entry(tool.0.clone());
576522
tool_telemetry = tool_telemetry.and_modify(|ev| ev.is_accepted = true);
577523

578524
let tool_start = std::time::Instant::now();
579525
queue!(
580526
self.output,
581527
style::SetForegroundColor(Color::Cyan),
582-
style::Print(format!("\n{}. {}...\n", i + 1, tool.1.display_name_action())),
528+
style::Print(format!("\n{}...\n", tool.1.display_name_action())),
583529
style::SetForegroundColor(Color::DarkGrey),
584530
style::Print(format!("{}\n", "▔".repeat(terminal_width))),
585531
style::SetForegroundColor(Color::Reset),
@@ -820,10 +766,7 @@ where
820766
if !tool_uses.is_empty() {
821767
Ok(ChatState::ValidateTools(tool_uses))
822768
} else {
823-
Ok(ChatState::PromptUser {
824-
tool_uses: None,
825-
tools_were_interrupted: false,
826-
})
769+
Ok(ChatState::PromptUser { tool_uses: None })
827770
}
828771
}
829772

@@ -916,15 +859,34 @@ where
916859
|| queued_tools.iter().all(|tool| !tool.1.requires_consent(&self.ctx));
917860

918861
if skip_consent {
862+
self.print_tool_descriptions(&queued_tools)?;
919863
Ok(ChatState::ExecuteTools(queued_tools))
920864
} else {
921865
Ok(ChatState::PromptUser {
922866
tool_uses: Some(queued_tools),
923-
tools_were_interrupted: false,
924867
})
925868
}
926869
}
927870

871+
fn print_tool_descriptions(&mut self, tool_uses: &[QueuedTool]) -> Result<(), ChatError> {
872+
let terminal_width = self.terminal_width();
873+
for (_, tool) in tool_uses.iter() {
874+
queue!(
875+
self.output,
876+
style::SetForegroundColor(Color::Cyan),
877+
style::Print(format!("{}\n", tool.display_name())),
878+
style::SetForegroundColor(Color::Reset),
879+
style::SetForegroundColor(Color::DarkGrey),
880+
style::Print(format!("{}\n", "▔".repeat(terminal_width))),
881+
style::SetForegroundColor(Color::Reset),
882+
)?;
883+
tool.queue_description(&self.ctx, &mut self.output)
884+
.map_err(|e| ChatError::Custom(format!("failed to print tool: {}", e).into()))?;
885+
queue!(self.output, style::Print("\n"))?;
886+
}
887+
Ok(())
888+
}
889+
928890
async fn send_tool_use_telemetry(&mut self) {
929891
for (_, mut event) in self.tool_use_telemetry_events.drain() {
930892
event.user_input_id = match self.tool_use_status {

0 commit comments

Comments
 (0)