vtcode 0.99.1

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
use crate::agent::runloop::unified::turn::context::TurnProcessingContext;

#[derive(Copy, Clone)]
pub(super) struct ToolCatalogCacheMetrics<'a> {
    pub step_count: usize,
    pub model: &'a str,
    pub cache_hit: bool,
    pub plan_mode: bool,
    pub request_user_input_enabled: bool,
    pub available_tools: usize,
    pub stable_prefix_hash: u64,
    pub tool_catalog_hash: Option<u64>,
    pub prefix_change_reason: &'a str,
}

pub(super) fn emit_tool_catalog_cache_metrics(
    ctx: &TurnProcessingContext<'_>,
    metrics: ToolCatalogCacheMetrics<'_>,
) {
    tracing::info!(
        target: "vtcode.turn.metrics",
        metric = "tool_catalog_cache",
        run_id = %ctx.harness_state.run_id.0,
        turn_id = %ctx.harness_state.turn_id.0,
        turn = metrics.step_count,
        model = metrics.model,
        cache_hit = metrics.cache_hit,
        plan_mode = metrics.plan_mode,
        request_user_input_enabled = metrics.request_user_input_enabled,
        available_tools = metrics.available_tools,
        stable_prefix_hash = metrics.stable_prefix_hash,
        tool_catalog_hash = metrics.tool_catalog_hash,
        prefix_change_reason = metrics.prefix_change_reason,
        "turn metric"
    );

    #[derive(serde::Serialize)]
    struct ToolCatalogCacheRecord<'a> {
        kind: &'static str,
        turn: usize,
        model: &'a str,
        cache_hit: bool,
        plan_mode: bool,
        request_user_input_enabled: bool,
        available_tools: usize,
        stable_prefix_hash: u64,
        tool_catalog_hash: Option<u64>,
        prefix_change_reason: &'a str,
        ts: i64,
    }

    ctx.traj.log(&ToolCatalogCacheRecord {
        kind: "tool_catalog_cache_metrics",
        turn: metrics.step_count,
        model: metrics.model,
        cache_hit: metrics.cache_hit,
        plan_mode: metrics.plan_mode,
        request_user_input_enabled: metrics.request_user_input_enabled,
        available_tools: metrics.available_tools,
        stable_prefix_hash: metrics.stable_prefix_hash,
        tool_catalog_hash: metrics.tool_catalog_hash,
        prefix_change_reason: metrics.prefix_change_reason,
        ts: chrono::Utc::now().timestamp(),
    });
}

#[allow(clippy::too_many_arguments)]
pub(super) fn emit_llm_retry_metrics(
    ctx: &TurnProcessingContext<'_>,
    step_count: usize,
    model: &str,
    plan_mode: bool,
    attempts_made: usize,
    max_retries: usize,
    success: bool,
    stream_fallback_used: bool,
    last_error_retryable: Option<bool>,
    last_error_preview: Option<&str>,
) {
    let retries_used = attempts_made.saturating_sub(1);
    let exhausted_retry_budget = !success && attempts_made >= max_retries;
    tracing::info!(
        target: "vtcode.turn.metrics",
        metric = "llm_retry_outcome",
        run_id = %ctx.harness_state.run_id.0,
        turn_id = %ctx.harness_state.turn_id.0,
        turn = step_count,
        model,
        plan_mode,
        attempts_made,
        retries_used,
        max_retries,
        success,
        exhausted_retry_budget,
        stream_fallback_used,
        last_error_retryable = last_error_retryable.unwrap_or(false),
        "turn metric"
    );

    #[derive(serde::Serialize)]
    struct LlmRetryMetricsRecord<'a> {
        kind: &'static str,
        turn: usize,
        model: &'a str,
        plan_mode: bool,
        attempts_made: usize,
        retries_used: usize,
        max_retries: usize,
        success: bool,
        exhausted_retry_budget: bool,
        stream_fallback_used: bool,
        last_error_retryable: Option<bool>,
        last_error: Option<&'a str>,
        ts: i64,
    }

    ctx.traj.log(&LlmRetryMetricsRecord {
        kind: "llm_retry_metrics",
        turn: step_count,
        model,
        plan_mode,
        attempts_made,
        retries_used,
        max_retries,
        success,
        exhausted_retry_budget,
        stream_fallback_used,
        last_error_retryable,
        last_error: last_error_preview,
        ts: chrono::Utc::now().timestamp(),
    });
}