claude-client 0.3.1

A Rust client for the Anthropic Claude API
Documentation
pub mod claude {
    use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE};
    use serde::{Deserialize, Serialize};
    use std::env;

    const ANTHROPIC_API_URL: &str = "https://api.anthropic.com/v1";
    const DEFAULT_MODEL: &str = "claude-3-7-sonnet-20250219";

    #[derive(Debug, Serialize)]
    pub struct Message {
        pub role: String,
        pub content: String,
    }

    #[derive(Debug, Serialize)]
    pub struct ClaudeRequest {
        pub model: String,
        pub messages: Vec<Message>,
        pub max_tokens: u32,
        pub system: Option<String>,
    }

    #[derive(Debug, Deserialize)]
    pub struct ClaudeResponse {
        pub content: Vec<Content>,
        pub role: String,
        pub model: String,
        pub id: String,
    }

    #[derive(Debug, Deserialize)]
    pub struct Content {
        pub text: String,
        #[serde(rename = "type")]
        pub content_type: String,
    }

    #[derive(Debug, Deserialize)]
    pub struct Model {
        #[serde(rename = "type")]
        pub model_type: String,
        pub id: String,
        pub display_name: String,
        pub created_at: String,
    }

    #[derive(Debug, Deserialize)]
    pub struct ListModelsResponse {
        pub data: Vec<Model>,
        pub has_more: bool,
        pub first_id: String,
        pub last_id: String,
    }

    pub struct ClaudeClient {
        client: reqwest::Client,
        api_key: String,
    }

    impl ClaudeClient {
        pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
            let api_key = env::var("ANTHROPIC_API_KEY")
                .map_err(|_| "ANTHROPIC_API_KEY environment variable not set")?;

            Ok(Self {
                client: reqwest::Client::new(),
                api_key,
            })
        }

        pub async fn send_message(
            &self,
            model: Option<&str>,
            system_prompt: &str,
            user_message: &str,
        ) -> Result<String, Box<dyn std::error::Error>> {
            let model = model.unwrap_or(DEFAULT_MODEL);
            let mut headers = HeaderMap::new();
            headers.insert(
                "x-api-key",
                HeaderValue::from_str(&self.api_key)
                    .map_err(|e| format!("Invalid API key format: {}", e))?,
            );
            headers.insert(
                "anthropic-api-key",
                HeaderValue::from_str(&self.api_key)
                    .map_err(|e| format!("Invalid API key format: {}", e))?,
            );
            headers.insert(
                "anthropic-version",
                HeaderValue::from_static("2023-06-01"),
            );
            headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
            headers.insert(
                CONTENT_TYPE,
                HeaderValue::from_static("application/json"),
            );

            let request = ClaudeRequest {
                model: model.to_string(),
                messages: vec![
                    Message {
                        role: "user".to_string(),
                        content: user_message.to_string(),
                    },
                ],
                max_tokens: 4000,
                system: Some(system_prompt.to_string()),
            };

            let raw_response = self
                .client
                .post(format!("{}/messages", ANTHROPIC_API_URL))
                .headers(headers)
                .json(&request)
                .send()
                .await?
                .text()
                .await?;
            println!("Raw response: {}", raw_response);
            let response: ClaudeResponse = serde_json::from_str(&raw_response)?;

            Ok(response.content.into_iter()
                .filter(|c| c.content_type == "text")
                .map(|c| c.text)
                .collect::<Vec<_>>()
                .join(""))
        }

        pub async fn list_models(&self) -> Result<Vec<Model>, Box<dyn std::error::Error>> {
            let mut headers = HeaderMap::new();
            headers.insert(
                "x-api-key",
                HeaderValue::from_str(&self.api_key)
                    .map_err(|e| format!("Invalid API key format: {}", e))?,
            );
            headers.insert(
                "anthropic-api-key",
                HeaderValue::from_str(&self.api_key)
                    .map_err(|e| format!("Invalid API key format: {}", e))?,
            );
            headers.insert(
                "anthropic-version",
                HeaderValue::from_static("2023-06-01"),
            );
            headers.insert(ACCEPT, HeaderValue::from_static("application/json"));

            let raw_response = self
                .client
                .get(format!("{}/models", ANTHROPIC_API_URL))
                .headers(headers)
                .send()
                .await?
                .text()
                .await?;
            println!("Raw response: {}", raw_response);
            let response: ListModelsResponse = serde_json::from_str(&raw_response)?;

            Ok(response.data)
        }
    }
}

#[cfg(test)]
mod tests;