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 anthropic_sdk::types::ContentBlock;
use reqwest::header::{HeaderMap, HeaderValue};
use std::time::Duration;

// Helper function to extract text content from response
fn extract_text_from_content(content: &[ContentBlock]) -> String {
    content.iter()
        .filter_map(|block| match block {
            ContentBlock::Text { text } => Some(text.as_str()),
            _ => None,
        })
        .collect::<Vec<_>>()
        .join(" ")
}

/// Custom HTTP client with eBay gateway authentication
struct EbayAnthropicClient {
    client: reqwest::Client,
    base_url: String,
    api_key: String,
}

impl EbayAnthropicClient {
    fn new(api_key: String, base_url: String) -> Self {
        let client = reqwest::Client::builder()
            .timeout(Duration::from_secs(30))
            .build()
            .unwrap();
            
        Self {
            client,
            base_url,
            api_key,
        }
    }
    
    /// Test different authentication header formats
    async fn test_auth_formats(&self) -> Result<(), Box<dyn std::error::Error>> {
        let url = format!("{}/messages", self.base_url.trim_end_matches('/'));
        
        let payload = serde_json::json!({
            "model": "hubgpt-chat-completions-sonnet-3-7",
            "max_tokens": 100,
            "messages": [{"role": "user", "content": "Hello! This is an auth test."}]
        });
        
        // Test 1: Bearer token
        println!("🔑 Testing Bearer token authentication...");
        match self.test_bearer_auth(&url, &payload).await {
            Ok(response) => {
                println!("✅ Bearer token works!");
                println!("📝 Response: {}", response);
                return Ok(());
            }
            Err(e) => println!("❌ Bearer token failed: {}", e),
        }
        
        // Test 2: Custom token header
        println!("\n🔑 Testing custom token header...");
        match self.test_token_header(&url, &payload).await {
            Ok(response) => {
                println!("✅ Token header works!");
                println!("📝 Response: {}", response);
                return Ok(());
            }
            Err(e) => println!("❌ Token header failed: {}", e),
        }
        
        // Test 3: Authorization header (direct)
        println!("\n🔑 Testing Authorization header (direct)...");
        match self.test_auth_header(&url, &payload).await {
            Ok(response) => {
                println!("✅ Authorization header works!");
                println!("📝 Response: {}", response);
                return Ok(());
            }
            Err(e) => println!("❌ Authorization header failed: {}", e),
        }
        
        println!("\n❌ None of the authentication methods worked.");
        println!("💡 Check your eBay gateway documentation for the correct authentication format.");
        
        Ok(())
    }
    
    async fn test_bearer_auth(&self, url: &str, payload: &serde_json::Value) -> Result<String, Box<dyn std::error::Error>> {
        let response = self.client
            .post(url)
            .header("Authorization", format!("Bearer {}", self.api_key))
            .header("Content-Type", "application/json")
            .header("anthropic-version", "2023-06-01")
            .json(payload)
            .send()
            .await?;
            
        if response.status().is_success() {
            let json: serde_json::Value = response.json().await?;
            Ok(extract_response_text(&json))
        } else {
            Err(format!("HTTP {}: {}", response.status(), response.text().await?).into())
        }
    }
    
    async fn test_token_header(&self, url: &str, payload: &serde_json::Value) -> Result<String, Box<dyn std::error::Error>> {
        let response = self.client
            .post(url)
            .header("token", &self.api_key)
            .header("Content-Type", "application/json")
            .header("anthropic-version", "2023-06-01")
            .json(payload)
            .send()
            .await?;
            
        if response.status().is_success() {
            let json: serde_json::Value = response.json().await?;
            Ok(extract_response_text(&json))
        } else {
            Err(format!("HTTP {}: {}", response.status(), response.text().await?).into())
        }
    }
    
    async fn test_auth_header(&self, url: &str, payload: &serde_json::Value) -> Result<String, Box<dyn std::error::Error>> {
        let response = self.client
            .post(url)
            .header("Authorization", &self.api_key)
            .header("Content-Type", "application/json")
            .header("anthropic-version", "2023-06-01")
            .json(payload)
            .send()
            .await?;
            
        if response.status().is_success() {
            let json: serde_json::Value = response.json().await?;
            Ok(extract_response_text(&json))
        } else {
            Err(format!("HTTP {}: {}", response.status(), response.text().await?).into())
        }
    }
}

fn extract_response_text(json: &serde_json::Value) -> String {
    json.get("content")
        .and_then(|content| content.as_array())
        .and_then(|blocks| blocks.first())
        .and_then(|block| block.get("text"))
        .and_then(|text| text.as_str())
        .unwrap_or("No text content found")
        .to_string()
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("🚀 eBay Gateway Custom Authentication Test");
    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. Please set ANTHROPIC_API_KEY or EBAY_ANTHROPIC_API_KEY");
            std::process::exit(1);
        });
    
    let base_url = "https://platformgateway2.vip.ebay.com/hubgptgatewaysvc/v1/anthropic";
    
    println!("📡 Testing eBay Gateway: {}", base_url);
    println!("🔑 API Key: {}...{}", &api_key[..4.min(api_key.len())], &api_key[api_key.len()-4.min(api_key.len())..]);
    println!();
    
    // Create custom client and test different auth methods
    let ebay_client = EbayAnthropicClient::new(api_key.clone(), base_url.to_string());
    ebay_client.test_auth_formats().await?;
    
    println!("\n" + "=".repeat(50));
    println!("🔧 SDK Modification Recommendations:");
    println!();
    
    println!("If Bearer token works, modify src/http/auth.rs:");
    println!("```rust");
    println!("// Change this line:");
    println!("headers.insert(\"x-api-key\", api_key_header);");
    println!("// To this:");
    println!("headers.insert(\"Authorization\", HeaderValue::from_str(&format!(\"Bearer {{}}\", self.api_key))?);");
    println!("```");
    
    println!("\nIf 'token' header works, modify src/http/auth.rs:");
    println!("```rust");
    println!("// Change this line:");
    println!("headers.insert(\"x-api-key\", api_key_header);");
    println!("// To this:");
    println!("headers.insert(\"token\", api_key_header);");
    println!("```");
    
    println!("\n📋 Next Steps:");
    println!("1. Run this with your real API key: cargo run --example custom_auth_ebay");
    println!("2. If one method works, I can modify the SDK auth handler");
    println!("3. Or create a custom configuration option for eBay gateway");
    
    Ok(())
}