codetether-rlm 0.1.0

Recursive Language Model processing for CodeTether
Documentation
//! Iterative RLM analysis loop — main entry.

use super::host::RouterHost;
use super::types::{CrateAutoProcessContext, LoopOutcome};
use crate::config::RlmConfig;
use crate::context_trace::ContextTrace;
use crate::traits::{LlmMessage, ToolDefinition};
use tracing::warn;

mod support;
mod text_path;
mod tool_dispatch;

/// Run the iterative analysis loop.
pub async fn run(
    ctx: &CrateAutoProcessContext<'_>,
    config: &RlmConfig,
    host: &mut dyn RouterHost,
    conversation: &mut Vec<LlmMessage>,
    trace: &mut ContextTrace,
    tools: &[ToolDefinition],
    summary_mode: bool,
) -> LoopOutcome {
    let mut iterations = 0;
    let mut subcalls = 0;
    let mut final_answer: Option<String> = None;
    let mut aborted = false;
    let mut last_error: Option<String> = None;

    for i in 0..config.max_iterations {
        iterations = i + 1;
        trace.next_iteration();
        support::emit_progress(ctx, iterations, config.max_iterations);
        if support::check_abort(ctx) {
            aborted = true;
            break;
        }
        let (provider, model) = support::active_provider(ctx, iterations);
        let response = match provider
            .complete(conversation.clone(), tools.to_vec(), &model, Some(0.7))
            .await
        {
            Ok(r) => r,
            Err(e) => {
                warn!(error = %e, iteration = iterations, "RLM: Model call failed");
                last_error = Some(format!("Model call failed at iteration {iterations}: {e}"));
                if iterations > 1 {
                    break;
                }
                return LoopOutcome {
                    final_answer: None,
                    iterations,
                    subcalls,
                    aborted: false,
                    last_error,
                };
            }
        };
        let response = support::maybe_rewrite(ctx, response, tools).await;
        conversation.push(LlmMessage::assistant_from(&response));
        if let Some(a) =
            tool_dispatch::try_tool_calls(host, &response, conversation, trace, summary_mode)
        {
            final_answer = Some(a);
            break;
        }
        if let Some(a) = text_path::try_text_path(&response, summary_mode, iterations) {
            final_answer = Some(a);
            break;
        }
        text_path::push_continuation(conversation, &response);
        subcalls += 1;
        if subcalls >= config.max_subcalls {
            warn!(subcalls, "RLM: Max subcalls reached");
            break;
        }
    }
    LoopOutcome {
        final_answer,
        iterations,
        subcalls,
        aborted,
        last_error,
    }
}