litellm-rs 0.6.0

A high-performance AI Gateway written in Rust, providing OpenAI-compatible APIs with intelligent routing, load balancing, and enterprise features
Documentation
use super::*;
use crate::core::budget::{ModelLimitConfig, ProviderLimitConfig, ResetPeriod};
use crate::core::keys::{CreateKeyConfig, InMemoryKeyRepository};
use crate::core::types::responses::{ChatDelta, ChatStreamChoice, FinishReason};

#[test]
fn empty_internal_stream_sentinel_is_not_candidate_output() {
    let mut chunk = ChatChunk {
        id: "stream-sentinel".to_string(),
        object: "chat.completion.chunk".to_string(),
        created: 0,
        model: "gemini-test".to_string(),
        choices: vec![],
        usage: None,
        system_fingerprint: None,
    };
    assert!(!stream_chunk_has_candidate_output(&chunk));

    let finish_only = ChatStreamChoice {
        index: 0,
        delta: ChatDelta::default(),
        finish_reason: Some(FinishReason::Stop),
        logprobs: None,
    };
    chunk.choices.push(finish_only);
    assert!(stream_chunk_has_candidate_output(&chunk));

    chunk.choices[0].finish_reason = None;
    chunk.choices[0].delta.content = Some("candidate output".to_string());
    assert!(stream_chunk_has_candidate_output(&chunk));
}

#[tokio::test]
async fn stream_disconnect_without_usage_records_reserved_key_cost() {
    let budget = UnifiedBudgetLimits::new();
    budget.providers.set_provider_limit(
        "openai",
        ProviderLimitConfig::new(1000.0, ResetPeriod::Monthly),
    );
    budget.models.set_model_limit(
        "gpt-4o",
        ModelLimitConfig::new(1000.0, ResetPeriod::Monthly),
    );
    let keys = KeyManager::new(InMemoryKeyRepository::new());
    let (key_id, _) = keys
        .generate_key(CreateKeyConfig {
            name: "stream key".to_string(),
            ..Default::default()
        })
        .await
        .expect("test key should be created");
    let reservation = reserve_completion_budget(&budget, "openai", "gpt-4o", 0, Some(100))
        .expect("reservation should succeed")
        .expect("priced model should reserve budget");
    let reserved = reservation.reserved_amount();

    record_stream_disconnect_spend_with_reservation(usage_spend_settlement(
        (&budget, &keys, Some(key_id)),
        ("openai", "gpt-4o", None),
        Some(reservation),
        None,
    ))
    .await;

    let stats = keys
        .get_usage_stats(key_id)
        .await
        .expect("usage stats should be readable");
    assert_eq!(stats.total_requests, 1);
    assert_eq!(stats.total_tokens, 0);
    assert_eq!(stats.total_cost, reserved);
}

#[tokio::test]
async fn finished_stream_without_usage_records_reserved_key_cost_after_output() {
    let budget = UnifiedBudgetLimits::new();
    budget.providers.set_provider_limit(
        "openai",
        ProviderLimitConfig::new(1000.0, ResetPeriod::Monthly),
    );
    budget.models.set_model_limit(
        "gpt-4o",
        ModelLimitConfig::new(1000.0, ResetPeriod::Monthly),
    );
    let keys = KeyManager::new(InMemoryKeyRepository::new());
    let (key_id, _) = keys
        .generate_key(CreateKeyConfig {
            name: "finished stream key".to_string(),
            ..Default::default()
        })
        .await
        .expect("test key should be created");
    let reservation = reserve_completion_budget(&budget, "openai", "gpt-4o", 0, Some(100))
        .expect("reservation should succeed")
        .expect("priced model should reserve budget");
    let reserved = reservation.reserved_amount();

    record_finished_stream_spend_with_reservation(StreamSpendSettlement {
        budget_limits: &budget,
        key_manager: &keys,
        api_key_id: Some(key_id),
        provider: "openai",
        model: "gpt-4o",
        pricing_provider: "openai",
        pricing_model: "gpt-4o",
        usage: None,
        saw_upstream_output: true,
        budget_reservation: Some(reservation),
        key_budget_reservation: None,
    })
    .await;

    assert_eq!(
        budget
            .providers
            .get_provider_usage("openai")
            .unwrap()
            .current_spend,
        reserved
    );
    assert_eq!(
        budget
            .models
            .get_model_usage("gpt-4o")
            .unwrap()
            .current_spend,
        reserved
    );
    let stats = keys
        .get_usage_stats(key_id)
        .await
        .expect("usage stats should be readable");
    assert_eq!(stats.total_requests, 1);
    assert_eq!(stats.total_tokens, 0);
    assert_eq!(stats.total_cost, reserved);
}