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 {
if !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;
if retry_delay_ms > STREAM_RETRY_MAX_MS {
retry_delay_ms = STREAM_RETRY_MAX_MS;
}
continue;
}
}
return result;
}
}
}