anthropic-sdk-rust 0.1.1

Comprehensive, type-safe Rust SDK for the Anthropic API with streaming, tools, vision, files, and batch processing support
Documentation
use anthropic_sdk::{Anthropic, ClientConfig, MessageCreateBuilder};
use std::time::Duration;
use reqwest::header::{HeaderMap, HeaderValue};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("๐Ÿ” Authentication Headers Diagnostic Tool");
    println!("==========================================\n");
    
    // Get API key from environment
    let api_key = std::env::var("ANTHROPIC_API_KEY")
        .or_else(|_| std::env::var("EBAY_ANTHROPIC_API_KEY"))
        .unwrap_or_else(|_| {
            println!("โ„น๏ธ  No API key found. Using 'test-key' for header analysis.");
            "test-key".to_string()
        });
    
    println!("๐Ÿ“‹ Testing Authentication Headers with eBay Gateway\n");
    
    // Test 1: Current SDK headers
    println!("๐Ÿงช Test 1: Current SDK Authentication Headers");
    test_current_headers(&api_key).await?;
    
    // Test 2: Alternative Authorization header
    println!("\n๐Ÿงช Test 2: Alternative Authorization Header Format");
    test_authorization_header(&api_key).await?;
    
    // Test 3: Bearer token format
    println!("\n๐Ÿงช Test 3: Bearer Token Format");
    test_bearer_token(&api_key).await?;
    
    // Test 4: Custom eBay headers
    println!("\n๐Ÿงช Test 4: Custom eBay Gateway Headers");
    test_custom_ebay_headers(&api_key).await?;
    
    println!("\n๐Ÿ’ก Recommendations:");
    println!("1. Check your eBay gateway API documentation for required headers");
    println!("2. The error 'token is missing in the header' suggests:");
    println!("   - Gateway expects 'Authorization: Bearer <token>' instead of 'x-api-key'");
    println!("   - Or a custom header like 'token' or 'access-token'");
    println!("3. You may need to modify the AuthHandler to use different headers");
    
    Ok(())
}

async fn test_current_headers(api_key: &str) -> Result<(), Box<dyn std::error::Error>> {
    println!("   Using standard Anthropic headers:");
    println!("   โ€ข x-api-key: {}", mask_key(api_key));
    println!("   โ€ข anthropic-version: 2023-06-01");
    println!("   โ€ข content-type: application/json");
    
    let config = ClientConfig::new(api_key)
        .with_base_url("https://platformgateway2.vip.ebay.com/hubgptgatewaysvc/v1/anthropic");
    
    let client = Anthropic::with_config(config)?;
    
    let response = client.messages()
        .create(
            MessageCreateBuilder::new("hubgpt-chat-completions-sonnet-3-7", 50)
                .user("Hello")
                .build()
        )
        .await;
    
    match response {
        Ok(_) => println!("   โœ… SUCCESS: Current headers work!"),
        Err(e) => println!("   โŒ FAILED: {}", e),
    }
    
    Ok(())
}

async fn test_authorization_header(api_key: &str) -> Result<(), Box<dyn std::error::Error>> {
    println!("   Testing Authorization header format:");
    println!("   โ€ข Authorization: {}", mask_key(api_key));
    
    // Create a custom HTTP client to test different headers
    let client = reqwest::Client::new();
    let url = "https://platformgateway2.vip.ebay.com/hubgptgatewaysvc/v1/anthropic/messages";
    
    let payload = serde_json::json!({
        "model": "hubgpt-chat-completions-sonnet-3-7",
        "max_tokens": 50,
        "messages": [{"role": "user", "content": "Hello"}]
    });
    
    let response = client
        .post(url)
        .header("Authorization", api_key)
        .header("Content-Type", "application/json")
        .header("anthropic-version", "2023-06-01")
        .json(&payload)
        .send()
        .await;
    
    match response {
        Ok(resp) => {
            if resp.status().is_success() {
                println!("   โœ… SUCCESS: Authorization header works!");
            } else {
                println!("   โŒ FAILED: HTTP {} - {}", resp.status(), resp.text().await.unwrap_or_default());
            }
        }
        Err(e) => println!("   โŒ CONNECTION ERROR: {}", e),
    }
    
    Ok(())
}

async fn test_bearer_token(api_key: &str) -> Result<(), Box<dyn std::error::Error>> {
    println!("   Testing Bearer token format:");
    println!("   โ€ข Authorization: Bearer {}", mask_key(api_key));
    
    let client = reqwest::Client::new();
    let url = "https://platformgateway2.vip.ebay.com/hubgptgatewaysvc/v1/anthropic/messages";
    
    let payload = serde_json::json!({
        "model": "hubgpt-chat-completions-sonnet-3-7",
        "max_tokens": 50,
        "messages": [{"role": "user", "content": "Hello"}]
    });
    
    let response = client
        .post(url)
        .header("Authorization", format!("Bearer {}", api_key))
        .header("Content-Type", "application/json")
        .header("anthropic-version", "2023-06-01")
        .json(&payload)
        .send()
        .await;
    
    match response {
        Ok(resp) => {
            if resp.status().is_success() {
                println!("   โœ… SUCCESS: Bearer token works!");
            } else {
                println!("   โŒ FAILED: HTTP {} - {}", resp.status(), resp.text().await.unwrap_or_default());
            }
        }
        Err(e) => println!("   โŒ CONNECTION ERROR: {}", e),
    }
    
    Ok(())
}

async fn test_custom_ebay_headers(api_key: &str) -> Result<(), Box<dyn std::error::Error>> {
    println!("   Testing custom eBay headers:");
    println!("   โ€ข token: {}", mask_key(api_key));
    println!("   โ€ข access-token: {}", mask_key(api_key));
    
    let client = reqwest::Client::new();
    let url = "https://platformgateway2.vip.ebay.com/hubgptgatewaysvc/v1/anthropic/messages";
    
    let payload = serde_json::json!({
        "model": "hubgpt-chat-completions-sonnet-3-7",
        "max_tokens": 50,
        "messages": [{"role": "user", "content": "Hello"}]
    });
    
    // Test common alternative header names
    let headers_to_test = vec![
        ("token", api_key),
        ("access-token", api_key),
        ("x-access-token", api_key),
        ("ebay-token", api_key),
        ("x-ebay-token", api_key),
    ];
    
    for (header_name, header_value) in headers_to_test {
        println!("   Testing: {} = {}", header_name, mask_key(header_value));
        
        let response = client
            .post(url)
            .header(header_name, header_value)
            .header("Content-Type", "application/json")
            .header("anthropic-version", "2023-06-01")
            .json(&payload)
            .send()
            .await;
        
        match response {
            Ok(resp) => {
                if resp.status().is_success() {
                    println!("   โœ… SUCCESS: {} header works!", header_name);
                    return Ok(());
                } else {
                    let status = resp.status();
                    let body = resp.text().await.unwrap_or_default();
                    println!("   โŒ {}: HTTP {} - {}", header_name, status, body);
                }
            }
            Err(e) => println!("   โŒ {}: CONNECTION ERROR: {}", header_name, e),
        }
    }
    
    Ok(())
}

fn mask_key(key: &str) -> String {
    if key.len() <= 8 {
        "*".repeat(key.len())
    } else {
        format!("{}...{}", &key[..4], &key[key.len()-4..])
    }
}