headless_lms_chatbot/azure_chatbot/client_tool_calls/
repair.rs1use headless_lms_models::chatbot_conversation_message_tool_calls::ChatbotConversationMessageToolCall;
5use headless_lms_models::chatbot_conversation_messages::UnansweredToolCallScope;
6use tracing::trace;
7
8use super::abort::ToolCallAbortReason;
9use crate::chatbot_error::ChatbotResult;
10use crate::prelude::*;
11
12pub(crate) async fn answer_unfinished_tool_calls(
17 conn: &mut PgConnection,
18 conversation_id: Uuid,
19 response_ids: &[String],
20) -> ChatbotResult<()> {
21 if response_ids.is_empty() {
22 return Ok(());
23 }
24 reap_unanswered_tool_calls(
25 conn,
26 conversation_id,
27 UnansweredToolCallScope::OwnTurn(response_ids),
28 )
29 .await?;
30 Ok(())
31}
32
33const HANGING_TOOL_CALL_REAP_AFTER_MINUTES: i64 = 10;
37
38pub(crate) async fn answer_stale_unfinished_tool_calls(
42 conn: &mut PgConnection,
43 conversation_id: Uuid,
44) -> ChatbotResult<Vec<ChatbotConversationMessageToolCall>> {
45 let cutoff = Utc::now() - chrono::Duration::minutes(HANGING_TOOL_CALL_REAP_AFTER_MINUTES);
46 reap_unanswered_tool_calls(
47 conn,
48 conversation_id,
49 UnansweredToolCallScope::AnyTurnOlderThan(cutoff),
50 )
51 .await
52}
53
54async fn reap_unanswered_tool_calls(
57 conn: &mut PgConnection,
58 conversation_id: Uuid,
59 scope: UnansweredToolCallScope<'_>,
60) -> ChatbotResult<Vec<ChatbotConversationMessageToolCall>> {
61 trace!(
62 "Dealing with unfinished tool calls for conversation {}",
63 conversation_id
64 );
65 headless_lms_models::chatbot_conversation_messages::answer_hanging_tool_call_messages_for_conversation(
66 conn,
67 conversation_id,
68 scope,
69 ToolCallAbortReason::Hanging.model_output(),
70 )
71 .await
72 .map_err(ChatbotError::from)
73}