vv-llm 0.2.2

Rust implementation surface for vv-llm
Documentation
use std::{env, path::PathBuf, time::Instant};
use vv_llm::{EndpointConfig, LlmSettings, ResolvedModelConfig, VvLlmError};

const TRUTHY: &[&str] = &["1", "true", "yes", "on"];

pub fn live_enabled() -> bool {
    env_bool("VV_LLM_RUN_LIVE_TESTS", false)
}

pub fn env_bool(name: &str, default: bool) -> bool {
    env::var(name)
        .ok()
        .map(|value| TRUTHY.contains(&value.trim().to_ascii_lowercase().as_str()))
        .unwrap_or(default)
}

pub fn load_live_settings(require_credentials: bool) -> Result<LlmSettings, VvLlmError> {
    let path = live_settings_path();
    let settings = LlmSettings::from_json_file(&path)?;
    let allow_empty = env_bool("VV_LLM_ALLOW_EMPTY_KEYS", false);
    if require_credentials && !allow_empty && !has_live_credentials(&settings) {
        return Err(VvLlmError::Configuration(format!(
            "live settings at {} have no usable credentials; create crates/vv-llm/tests/fixtures/dev_settings.json or set VV_LLM_ALLOW_EMPTY_KEYS=1",
            path.display()
        )));
    }
    Ok(settings)
}

pub fn live_settings_path() -> PathBuf {
    env::var("VV_LLM_SETTINGS_JSON")
        .map(PathBuf::from)
        .unwrap_or_else(|_| {
            let dev = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
                .join("tests")
                .join("fixtures")
                .join("dev_settings.json");
            if dev.exists() {
                dev
            } else {
                PathBuf::from(env!("CARGO_MANIFEST_DIR"))
                    .join("tests")
                    .join("fixtures")
                    .join("sample_settings.json")
            }
        })
}

pub fn has_live_credentials(settings: &LlmSettings) -> bool {
    settings.endpoints.iter().any(endpoint_has_live_credentials)
}

pub fn endpoint_has_live_credentials(endpoint: &EndpointConfig) -> bool {
    let has_api_key = endpoint
        .api_key
        .as_deref()
        .map(|key| {
            let key = key.trim();
            !key.is_empty() && !key.starts_with("YOUR_")
        })
        .unwrap_or(false);
    let has_bedrock_credentials = endpoint
        .credentials
        .get("access_key")
        .and_then(serde_json::Value::as_str)
        .map(has_real_secret)
        .unwrap_or(false)
        && endpoint
            .credentials
            .get("secret_key")
            .and_then(serde_json::Value::as_str)
            .map(has_real_secret)
            .unwrap_or(false);
    let has_vertex_refresh_credentials = endpoint
        .credentials
        .get("refresh_token")
        .and_then(serde_json::Value::as_str)
        .map(has_real_secret)
        .unwrap_or(false)
        && endpoint
            .credentials
            .get("client_id")
            .and_then(serde_json::Value::as_str)
            .map(has_real_secret)
            .unwrap_or(false)
        && endpoint
            .credentials
            .get("client_secret")
            .and_then(serde_json::Value::as_str)
            .map(has_real_secret)
            .unwrap_or(false);
    let has_vertex_service_account = endpoint
        .credentials
        .get("private_key")
        .and_then(serde_json::Value::as_str)
        .map(has_real_secret)
        .unwrap_or(false)
        && endpoint
            .credentials
            .get("client_email")
            .and_then(serde_json::Value::as_str)
            .map(has_real_secret)
            .unwrap_or(false);
    has_api_key
        || has_bedrock_credentials
        || has_vertex_refresh_credentials
        || has_vertex_service_account
}

fn has_real_secret(value: &str) -> bool {
    let value = value.trim();
    !value.is_empty() && !value.starts_with("YOUR_")
}

pub fn run_with_timer<T>(label: &str, operation: impl FnOnce() -> T) -> T {
    let start = Instant::now();
    let result = operation();
    eprintln!(
        "[live] {label} finished in {:.2}s",
        start.elapsed().as_secs_f32()
    );
    result
}

pub fn require_live() {
    if !live_enabled() {
        eprintln!("Live tests are disabled. Set VV_LLM_RUN_LIVE_TESTS=1 to run.");
    }
}

pub fn resolved_parts(resolved: ResolvedModelConfig) -> (String, String, String) {
    (
        resolved.model_id,
        resolved.endpoint.api_base.unwrap_or_default(),
        resolved.endpoint.api_key.unwrap_or_default(),
    )
}