use std::sync::{Arc, Mutex};
use tokio::sync::broadcast;
use crate::engine::runtime::llm_engine::LlmTurnResult;
use crate::engine::runtime::plan_runner::RuntimeCore;
use crate::types::{AgentResult, RuntimeEvent, SessionId};
const STREAM_RETRY_MAX: u32 = 3;
const STREAM_RETRY_INITIAL_MS: u64 = 1_000;
const STREAM_RETRY_MAX_MS: u64 = 10_000;
const STREAM_RETRY_BACKOFF: f64 = 2.0;
impl RuntimeCore {
#[allow(clippy::too_many_arguments)]
pub(super) async fn llm_call_with_retry<F>(
&self,
session_id: &SessionId,
messages: &[crate::types::ChatMessage],
tools: &[serde_json::Value],
config: &crate::types::AgentConfig,
thinking_disabled: bool,
turn_count: u32,
event_rx: &mut broadcast::Receiver<RuntimeEvent>,
on_event: Arc<Mutex<F>>,
) -> AgentResult<LlmTurnResult>
where
F: FnMut(RuntimeEvent) -> AgentResult<()> + Send + 'static,
{
let mut retry_left: u32 = STREAM_RETRY_MAX;
let mut retry_delay_ms: u64 = STREAM_RETRY_INITIAL_MS;
loop {
let stream = match config.llm.llm_retry.as_ref() {
Some(retry) => {
tracing::debug!(
session_id = session_id.id,
turn = turn_count,
"LLM: using retry mode"
);
self.llm_engine
.run_llm_turn_with_retry(
session_id,
messages,
tools,
config.reasoning.as_ref(),
config.llm.response_format.as_ref(),
retry.clone(),
thinking_disabled,
)
.await?
}
None => {
tracing::debug!(
session_id = session_id.id,
turn = turn_count,
"LLM: calling chat_stream"
);
self.llm_engine
.chat_stream(
messages,
tools,
config.reasoning.as_ref(),
config.llm.response_format.as_ref(),
thinking_disabled,
)
.await?
}
};
tracing::info!(
session_id = session_id.id,
turn = turn_count,
"LLM stream obtained, processing"
);
let span =
tracing::info_span!("llm_turn", session_id = session_id.id, turn = turn_count);
let cancel_token = self.cancel_token();
let result = self
.llm_engine
.process_stream(
session_id,
stream,
span,
event_rx,
on_event.clone(),
&cancel_token,
)
.await;
tracing::info!(
session_id = session_id.id,
turn = turn_count,
is_err = result.is_err(),
"LLM stream processed"
);
if let Err(ref e) = result
&& !e.is_cancelled()
&& retry_left > 0
{
retry_left -= 1;
tracing::warn!(
session_id = session_id.id,
turn = turn_count,
error = %e,
retries_left = retry_left,
delay_ms = retry_delay_ms,
"SSE stream interrupted, retrying..."
);
tokio::time::sleep(std::time::Duration::from_millis(retry_delay_ms)).await;
retry_delay_ms = (retry_delay_ms as f64 * STREAM_RETRY_BACKOFF) as u64;
retry_delay_ms = retry_delay_ms.min(STREAM_RETRY_MAX_MS);
continue;
}
return result;
}
}
}