Skip to content

Commit 76ce78d

Browse files
fix: add check for tool use timeouts (#1324)
1 parent 8fb0cf1 commit 76ce78d

File tree

2 files changed

+30
-16
lines changed

2 files changed

+30
-16
lines changed

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2569,16 +2569,29 @@ impl ChatContext {
25692569
tool_use_id,
25702570
name,
25712571
message,
2572+
time_elapsed,
25722573
} => {
25732574
error!(
25742575
recv_error.request_id,
25752576
tool_use_id, name, "The response stream ended before the entire tool use was received"
25762577
);
25772578
if self.interactive {
2578-
execute!(self.output, cursor::Hide)?;
2579+
drop(self.spinner.take());
2580+
queue!(
2581+
self.output,
2582+
terminal::Clear(terminal::ClearType::CurrentLine),
2583+
cursor::MoveToColumn(0),
2584+
style::SetForegroundColor(Color::Yellow),
2585+
style::SetAttribute(Attribute::Bold),
2586+
style::Print(format!(
2587+
"Warning: received an unexpected error from the model after {:.2}s\n\n",
2588+
time_elapsed.as_secs_f64()
2589+
)),
2590+
style::SetAttribute(Attribute::Reset),
2591+
)?;
25792592
self.spinner = Some(Spinner::new(
25802593
Spinners::Dots,
2581-
"The generated tool use was too large, trying to divide up the work...".to_string(),
2594+
"Trying to divide up the work...".to_string(),
25822595
));
25832596
}
25842597

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

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::time::Instant;
1+
use std::time::{
2+
Duration,
3+
Instant,
4+
};
25

36
use eyre::Result;
47
use fig_api_client::clients::SendMessageOutput;
@@ -66,6 +69,7 @@ pub enum RecvErrorKind {
6669
tool_use_id: String,
6770
name: String,
6871
message: Box<AssistantMessage>,
72+
time_elapsed: Duration,
6973
},
7074
}
7175

@@ -180,29 +184,29 @@ impl ResponseParser {
180184
/// The arguments are the fields from the first [ChatResponseStream::ToolUseEvent] consumed.
181185
async fn parse_tool_use(&mut self, id: String, name: String) -> Result<AssistantToolUse, RecvError> {
182186
let mut tool_string = String::new();
183-
let mut stop_seen = false;
184187
let start = Instant::now();
185188
while let Some(ChatResponseStream::ToolUseEvent { .. }) = self.peek().await? {
186189
if let Some(ChatResponseStream::ToolUseEvent { input, stop, .. }) = self.next().await? {
187190
if let Some(i) = input {
188191
tool_string.push_str(&i);
189192
}
190193
if let Some(true) = stop {
191-
stop_seen = true;
192194
break;
193195
}
194196
}
195197
}
196198
let args = match serde_json::from_str(&tool_string) {
197199
Ok(args) => args,
198200
Err(err) => {
199-
// If the stream ended before we saw the final tool use event (and thus failed
200-
// deserializing the tool use), this is most likely due to the backend dropping the
201-
// connection. The tool was too large!
202-
if self.peek().await?.is_none() && !stop_seen {
201+
// If we failed deserializing after waiting for a long time, then this is most
202+
// likely bedrock responding with a stop event for some reason without actually
203+
// including the tool contents. Essentially, the tool was too large.
204+
// Timeouts have been seen as short as ~1 minute, so setting the time to 30.
205+
let time_elapsed = start.elapsed();
206+
if self.peek().await?.is_none() && time_elapsed > Duration::from_secs(30) {
203207
error!(
204208
"Received an unexpected end of stream after spending ~{}s receiving tool events",
205-
Instant::now().duration_since(start).as_secs_f64()
209+
time_elapsed.as_secs_f64()
206210
);
207211
self.tool_uses.push(AssistantToolUse {
208212
id: id.clone(),
@@ -211,18 +215,14 @@ impl ResponseParser {
211215
[(
212216
"key".to_string(),
213217
serde_json::Value::String(
214-
"fake tool use args - actual tool use was too large to include".to_string(),
218+
"WARNING: the actual tool use arguments were too complicated to be generated"
219+
.to_string(),
215220
),
216221
)]
217222
.into_iter()
218223
.collect(),
219224
),
220225
});
221-
// let message = Box::new(AssistantMessage {
222-
// message_id: Some(self.message_id.clone()),
223-
// content: std::mem::take(&mut self.assistant_text),
224-
// tool_uses: Some(self.tool_uses.clone().into_iter().map(Into::into).collect()),
225-
// });
226226
let message = Box::new(AssistantMessage::new_tool_use(
227227
Some(self.message_id.clone()),
228228
std::mem::take(&mut self.assistant_text),
@@ -232,6 +232,7 @@ impl ResponseParser {
232232
tool_use_id: id,
233233
name,
234234
message,
235+
time_elapsed,
235236
}));
236237
} else {
237238
return Err(self.error(err));

0 commit comments

Comments
 (0)