litellm-rs 0.8.2

A high-performance AI Gateway written in Rust, providing OpenAI-compatible APIs with intelligent routing, load balancing, and enterprise features
Documentation
use std::time::Duration;

use actix_web::{http::StatusCode, test as actix_test, web};

use super::*;
use crate::core::traits::integration::{LlmEndEvent, LlmStartEvent};

#[tokio::test]
async fn metrics_endpoint_exposes_live_callback_sample_from_same_runtime() {
    let _metrics_guard = MetricsMiddleware::test_lock().await;
    MetricsMiddleware::reset_for_tests();
    crate::server::middleware::reset_unpriced_metrics_for_tests();
    crate::server::middleware::record_unpriced_event(
        "metrics-http-provider",
        "tenant-http-private-model",
        "reject",
        "reject_preflight",
    );

    let mut config = Config::default();
    config
        .gateway
        .providers
        .push(crate::config::models::provider::ProviderConfig {
            name: "metrics-test-openai".to_string(),
            provider_type: "openai".to_string(),
            api_key: "sk-metrics-test".to_string(),
            ..Default::default()
        });
    config.gateway.auth.enable_jwt = false;
    config.gateway.auth.enable_api_key = false;
    config.gateway.auth.allow_anonymous = true;
    config.gateway.storage.database.enabled = false;
    config.gateway.storage.redis.enabled = false;
    config.gateway.pricing.source = None;

    let server = HttpServer::new(&config)
        .await
        .unwrap_or_else(|error| panic!("server startup failed: {error}"));
    let app = actix_test::init_service(HttpServer::create_app(web::Data::new(
        server.state().clone(),
    )))
    .await;

    let health_req = actix_test::TestRequest::get().uri("/health").to_request();
    let health_resp = actix_test::call_service(&app, health_req).await;
    assert_eq!(health_resp.status(), StatusCode::OK);
    drop(actix_test::read_body(health_resp).await);

    let start = LlmStartEvent::new("live-request", "gpt-live").provider("openai-live");
    let metrics = server
        .state()
        .callbacks
        .begin_llm_metrics(&start)
        .expect("live Prometheus metrics lifecycle should start");
    metrics.emit_end(&LlmEndEvent::new("live-request", "gpt-live").provider("openai-live"));

    let expected = "litellm_requests_total{model=\"gpt-live\",provider=\"openai-live\"} 1";
    let body = tokio::time::timeout(Duration::from_secs(1), async {
        loop {
            let request = actix_test::TestRequest::get().uri("/metrics").to_request();
            let response = actix_test::call_service(&app, request).await;
            assert_eq!(response.status(), StatusCode::OK);
            let body = String::from_utf8(actix_test::read_body(response).await.to_vec())
                .expect("metrics response should be UTF-8");
            if body.contains(expected) {
                break body;
            }
            tokio::time::sleep(Duration::from_millis(1)).await;
        }
    })
    .await
    .expect("live callback metric should become scrape-visible");

    assert!(body.contains("gateway_http_requests_total 1"));
    assert!(body.contains("gateway_http_responses_total{class=\"2xx\"} 1"));
    assert!(body.contains(
        "gateway_unpriced_events_total{provider=\"metrics-http-provider\",model_bucket=\"other\",policy=\"reject\",outcome=\"reject_preflight\"} 1"
    ));
    assert!(!body.contains("tenant-http-private-model"));
}