llmshim 0.7.0

Blazing fast LLM API translation layer in pure Rust
Documentation
use llmshim::provider::Provider;
use llmshim::providers::{anthropic::Anthropic, gemini::Gemini, openai::OpenAi, xai::Xai};
use serde_json::{json, Value};

fn check(provider: &dyn Provider, template: Value, pointer: &str, bad: &[Value]) {
    for value in bad {
        let mut response = template.clone();
        *response.pointer_mut(pointer).unwrap() = value.clone();
        let error = provider
            .transform_response("test-model", response)
            .unwrap_err();
        assert!(!error.to_string().contains("private"));
    }
    let mut response = template;
    let (parent, key) = pointer.rsplit_once('/').unwrap();
    response
        .pointer_mut(parent)
        .unwrap()
        .as_object_mut()
        .unwrap()
        .remove(key);
    assert!(provider.transform_response("test-model", response).is_err());
}

#[test]
fn openai_nonterminal_or_missing_status_cannot_become_stop() {
    check_responses_api(&OpenAi::new("unused".into()));
}

#[test]
fn xai_nonterminal_or_missing_status_cannot_become_stop() {
    check_responses_api(&Xai::new("unused".into()));
}

fn check_responses_api(provider: &dyn Provider) {
    let response = json!({"status":"completed","output":[{"type":"message","content":[{"type":"output_text","text":"private partial text"}]}]});
    check(
        provider,
        response.clone(),
        "/status",
        &[
            json!("failed"),
            json!("cancelled"),
            json!("queued"),
            json!("in_progress"),
            json!("private-unknown"),
            Value::Null,
            json!(42),
        ],
    );
    for (status, expected) in [("completed", "stop"), ("incomplete", "length")] {
        let mut value = response.clone();
        value["status"] = json!(status);
        assert_eq!(
            provider.transform_response("test-model", value).unwrap()["choices"][0]
                ["finish_reason"],
            expected
        );
    }
}

#[test]
fn gemini_unknown_or_missing_reason_cannot_become_stop() {
    let provider = Gemini::new("unused".into());
    let response = json!({"candidates":[{"finishReason":"STOP","content":{"parts":[{"text":"private partial text"}]}}]});
    check(
        &provider,
        response.clone(),
        "/candidates/0/finishReason",
        &[
            json!("RECITATION"),
            json!("MALFORMED_FUNCTION_CALL"),
            json!("OTHER"),
            json!("private-unknown"),
            Value::Null,
            json!(42),
        ],
    );
    for (reason, expected) in [
        ("STOP", "stop"),
        ("MAX_TOKENS", "length"),
        ("SAFETY", "content_filter"),
    ] {
        let mut value = response.clone();
        value["candidates"][0]["finishReason"] = json!(reason);
        assert_eq!(
            provider.transform_response("test-model", value).unwrap()["choices"][0]
                ["finish_reason"],
            expected
        );
    }
}

#[test]
fn anthropic_missing_or_unknown_reason_cannot_become_stop() {
    let provider = Anthropic::new("unused".into());
    let response =
        json!({"stop_reason":"end_turn","content":[{"type":"text","text":"private partial text"}]});
    check(
        &provider,
        response.clone(),
        "/stop_reason",
        &[
            json!("pause_turn"),
            json!("private-unknown"),
            Value::Null,
            json!(42),
        ],
    );
    for (reason, expected) in [
        ("end_turn", "stop"),
        ("stop_sequence", "stop"),
        ("max_tokens", "length"),
        ("tool_use", "tool_calls"),
    ] {
        let mut value = response.clone();
        value["stop_reason"] = json!(reason);
        assert_eq!(
            provider.transform_response("test-model", value).unwrap()["choices"][0]
                ["finish_reason"],
            expected
        );
    }
}