vv-agent 0.6.2

VectorVein agent runtime, SDK, CLI, tools, and workspace backends
Documentation
use super::*;

#[test]
fn vv_llm_client_fails_over_to_next_endpoint_client() {
    let llm = VvLlmClient::new_with_named_endpoint_clients(
        "openai",
        "gpt-4o-alias",
        "gpt-4o-mini",
        vec![
            (
                "primary-endpoint".to_string(),
                "gpt-4o-mini".to_string(),
                Box::new(FailingChatClient) as Box<dyn vv_llm::ChatClient>,
            ),
            (
                "backup-endpoint".to_string(),
                "gpt-4o-mini-backup".to_string(),
                Box::new(UsageMissingChatClient) as Box<dyn vv_llm::ChatClient>,
            ),
        ],
        90.0,
    )
    .with_randomize_endpoints(false)
    .with_retry_policy(1, 0.0);

    let response = llm
        .complete(LlmRequest::new(
            "gpt-4o-alias",
            vec![Message::user("fall over to backup endpoint")],
        ))
        .expect("completion from fallback endpoint");

    assert_eq!(response.content, "estimated usage response");
    assert_eq!(response.raw["used_endpoint_id"], json!("backup-endpoint"));
    assert_eq!(response.raw["used_model_id"], json!("gpt-4o-mini-backup"));
    assert_eq!(response.raw["stream_mode"], json!(true));
}

#[test]
fn vv_llm_client_prefers_last_successful_endpoint() {
    let primary = CountingFailingChatClient::default();
    let primary_probe = primary.clone();
    let llm = VvLlmClient::new_with_named_endpoint_clients(
        "openai",
        "gpt-4o-mini",
        "gpt-4o-mini",
        vec![
            (
                "primary-endpoint".to_string(),
                "gpt-4o-mini".to_string(),
                Box::new(primary) as Box<dyn vv_llm::ChatClient>,
            ),
            (
                "backup-endpoint".to_string(),
                "gpt-4o-mini".to_string(),
                Box::new(UsageMissingChatClient) as Box<dyn vv_llm::ChatClient>,
            ),
        ],
        90.0,
    )
    .with_randomize_endpoints(false)
    .with_retry_policy(1, 0.0);

    let first = llm
        .complete(LlmRequest::new("gpt-4o-mini", vec![Message::user("first")]))
        .expect("first fallback completion");
    let second = llm
        .complete(LlmRequest::new(
            "gpt-4o-mini",
            vec![Message::user("second")],
        ))
        .expect("second preferred completion");

    assert_eq!(first.raw["used_endpoint_id"], json!("backup-endpoint"));
    assert_eq!(second.raw["used_endpoint_id"], json!("backup-endpoint"));
    assert_eq!(primary_probe.calls(), 1);
}

#[test]
fn vv_llm_client_exposes_agent_endpoint_randomization_policy() {
    let llm = VvLlmClient::new_with_named_endpoint_clients(
        "openai",
        "gpt-4o-mini",
        "gpt-4o-mini",
        vec![
            (
                "primary-endpoint".to_string(),
                "gpt-4o-mini".to_string(),
                Box::new(UsageMissingChatClient) as Box<dyn vv_llm::ChatClient>,
            ),
            (
                "backup-endpoint".to_string(),
                "gpt-4o-mini".to_string(),
                Box::new(UsageMissingChatClient) as Box<dyn vv_llm::ChatClient>,
            ),
        ],
        90.0,
    );

    assert!(llm.randomize_endpoints());
    let llm = llm.with_randomize_endpoints(false);
    assert!(!llm.randomize_endpoints());
}

#[test]
fn vv_llm_client_retries_endpoint_before_failover() {
    let flaky = FlakyChatClient::new(1);
    let flaky_probe = flaky.clone();
    let llm = VvLlmClient::new(
        "openai",
        "gpt-4o-mini",
        "gpt-4o-mini",
        Box::new(flaky),
        90.0,
    )
    .with_retry_policy(2, 0.0);

    let response = llm
        .complete(LlmRequest::new(
            "gpt-4o-mini",
            vec![Message::user("retry once")],
        ))
        .expect("retry succeeds");

    assert_eq!(response.content, "flaky success");
    assert_eq!(flaky_probe.calls(), 2);
}

#[test]
fn vv_llm_client_uses_endpoint_model_for_selected_alias() {
    let llm = VvLlmClient::new_with_named_endpoint_clients(
        "openai",
        "gpt-alias",
        "gpt-provider-model",
        vec![(
            "primary-endpoint".to_string(),
            "gpt-provider-model".to_string(),
            Box::new(UsageMissingChatClient) as Box<dyn vv_llm::ChatClient>,
        )],
        90.0,
    );

    let response = llm
        .complete(LlmRequest::new(
            "gpt-alias",
            vec![Message::user("use provider model id")],
        ))
        .expect("completion from primary endpoint");

    assert_eq!(response.raw["used_model_id"], json!("gpt-provider-model"));
}