doum_cli/llm/
retry.rs

1use crate::system::error::{DoumError, DoumResult};
2use std::future::Future;
3
4/// LLM 호출 및 파싱 재시도
5///
6/// LLM이 잘못된 형식으로 응답하는 경우를 대비하여 재시도 로직을 제공합니다.
7pub async fn retry_with_parse<T, F, Fut, P>(
8    llm_call: F,
9    parser: P,
10    max_retries: u32,
11) -> DoumResult<T>
12where
13    F: Fn() -> Fut,
14    Fut: Future<Output = DoumResult<String>>,
15    P: Fn(&str) -> DoumResult<T>,
16{
17    let mut last_error = None;
18
19    for attempt in 1..=max_retries {
20        // LLM 호출
21        let response = match llm_call().await {
22            Ok(resp) => resp,
23            Err(e) => {
24                last_error = Some(e);
25                if attempt < max_retries {
26                    tracing::warn!(
27                        "LLM call failed (attempt {}/{}): Retrying...",
28                        attempt,
29                        max_retries
30                    );
31                    continue;
32                } else {
33                    tracing::error!("All retry attempts exhausted.");
34                    break;
35                }
36            }
37        };
38
39        // 파싱 시도
40        match parser(&response) {
41            Ok(parsed) => return Ok(parsed),
42            Err(e) => {
43                last_error = Some(e);
44                if attempt < max_retries {
45                    tracing::warn!(
46                        "Parse failed (attempt {}/{}): Retrying...",
47                        attempt,
48                        max_retries
49                    );
50                    continue;
51                } else {
52                    tracing::error!("All retry attempts exhausted.");
53                    break;
54                }
55            }
56        }
57    }
58
59    // 모든 재시도 실패
60    Err(last_error.unwrap_or_else(|| DoumError::LLM("Unknown error after retries".to_string())))
61}