Anthropic Rust SDK

A modern, idiomatic Rust SDK for the Anthropic API, providing type-safe, async-first access to Claude models.
Features
- 🦀 Idiomatic Rust: Built with Rust best practices, leveraging the type system for safety
- ⚡ Async/Await: Full async support with tokio compatibility
- 🔄 Streaming: Real-time streaming of Claude's responses
- 🛠️ Tool Calling: Complete support for Claude's tool use capabilities
- 🖼️ Multimodal: Send images and documents to Claude
- 🔧 Builder Pattern: Fluent APIs for constructing requests
- 📊 Token Counting: Estimate costs before making requests
- 🔄 Retry Logic: Built-in exponential backoff for transient errors
- 📝 Comprehensive Docs: Extensive documentation and examples
Installation
Add this to your Cargo.toml:
[dependencies]
anthropic_rust = "0.1.1"
tokio = { version = "1.0", features = ["full"] }
Quick Start
use anthropic_rust::{Client, Model, ContentBlock};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new(Model::Claude35Sonnet20241022)?;
let request = client.chat_builder()
.user_message(ContentBlock::text("Hello, Claude!"))
.build();
let response = client.execute_chat(request).await?;
for content in response.content {
if let ContentBlock::Text { text, .. } = content {
println!("Claude: {}", text);
}
}
Ok(())
}
Authentication
Set your API key as an environment variable:
export ANTHROPIC_API_KEY="your-api-key-here"
Or configure it explicitly:
let client = Client::builder()
.api_key("your-api-key")
.model(Model::Claude35Sonnet20241022)
.build()?;
Examples
Basic Conversation
use anthropic_rust::{Client, Model, ContentBlock, Role, MessageParam};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new(Model::Claude35Sonnet20241022)?;
let request = client.chat_builder()
.system("You are a helpful assistant.")
.user_message(ContentBlock::text("What's the capital of France?"))
.assistant_message(ContentBlock::text("The capital of France is Paris."))
.user_message(ContentBlock::text("What's its population?"))
.build();
let response = client.execute_chat(request).await?;
if let Some(ContentBlock::Text { text, .. }) = response.content.first() {
println!("Claude: {}", text);
}
Ok(())
}
Streaming Responses
use anthropic_rust::{Client, Model, ContentBlock, StreamEvent};
use futures::StreamExt;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new(Model::Claude35Sonnet20241022)?;
let request = client.chat_builder()
.user_message(ContentBlock::text("Write a short poem about Rust"))
.build();
let mut stream = client.stream_chat(request).await?;
print!("Claude: ");
while let Some(event) = stream.next().await {
match event? {
StreamEvent::ContentBlockDelta { delta, .. } => {
if let anthropic_rust::ContentDelta::TextDelta { text } = delta {
print!("{}", text);
}
}
StreamEvent::MessageStop => break,
_ => {}
}
}
println!();
Ok(())
}
Tool Calling
use anthropic_rust::{Client, Model, ContentBlock, Tool};
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new(Model::Claude35Sonnet20241022)?;
let weather_tool = Tool::new("get_weather")
.description("Get current weather for a location")
.schema_value(json!({
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City name"
}
},
"required": ["location"]
}))
.build();
let request = client.chat_builder()
.user_message(ContentBlock::text("What's the weather in San Francisco?"))
.tool(weather_tool)
.build();
let response = client.execute_chat(request).await?;
for content in response.content {
match content {
ContentBlock::Text { text, .. } => {
println!("Claude: {}", text);
}
ContentBlock::ToolUse { name, input, .. } => {
println!("Claude wants to use tool: {} with input: {}", name, input);
}
_ => {}
}
}
Ok(())
}
Multimodal (Images)
use anthropic_rust::{Client, Model, ContentBlock, ImageMediaType};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new(Model::Claude35Sonnet20241022)?;
let image_data = std::fs::read("path/to/image.jpg")?;
let base64_image = base64::encode(&image_data);
let request = client.chat_builder()
.user_message(ContentBlock::image_base64(
ImageMediaType::Jpeg,
base64_image
))
.user_message(ContentBlock::text("What do you see in this image?"))
.build();
let response = client.execute_chat(request).await?;
if let Some(ContentBlock::Text { text, .. }) = response.content.first() {
println!("Claude: {}", text);
}
Ok(())
}
Advanced Configuration
use anthropic_rust::{Client, Model, RetryConfig};
use std::time::Duration;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::builder()
.api_key("your-api-key")
.model(Model::Claude35Sonnet20241022)
.max_tokens(2000)
.timeout(Duration::from_secs(30))
.retry_config(RetryConfig {
max_retries: 5,
initial_delay: Duration::from_millis(1000),
max_delay: Duration::from_secs(60),
backoff_multiplier: 2.0,
})
.build()?;
Ok(())
}
Token Counting
use anthropic_rust::{Client, Model, ContentBlock, types::{CountTokensRequest, MessageParam, Role}};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new(Model::Claude35Sonnet20241022)?;
let request = CountTokensRequest {
messages: vec![
MessageParam {
role: Role::User,
content: vec![ContentBlock::text("How many tokens is this message?")],
}
],
system: None,
tools: None,
};
let token_count = client.count_tokens(request).await?;
println!("This message uses {} input tokens", token_count.input_tokens);
Ok(())
}
Error Handling
The SDK provides comprehensive error handling with detailed error types:
use anthropic_rust::{Client, Model, Error};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::new(Model::Claude35Sonnet20241022)?;
match client.execute_chat(request).await {
Ok(response) => {
println!("Success: {:?}", response);
}
Err(Error::Authentication(msg)) => {
eprintln!("Authentication failed: {}", msg);
}
Err(Error::RateLimit { retry_after }) => {
eprintln!("Rate limited. Retry after: {:?}", retry_after);
}
Err(Error::Network(err)) => {
eprintln!("Network error: {}", err);
}
Err(err) => {
eprintln!("Other error: {}", err);
}
}
Ok(())
}
Model Selection
Choose the right model for your use case:
| Model |
Best For |
Speed |
Cost |
Claude3Haiku20240307 |
Simple tasks, fast responses |
Fastest |
Lowest |
Claude35Sonnet20241022 |
Balanced performance |
Medium |
Medium |
Claude3Opus20240229 |
Complex reasoning, analysis |
Slower |
Higher |
let client = Client::new(Model::Claude35Sonnet20241022)?;
let simple_response = client.execute_chat_with_model(
Model::Claude3Haiku20240307,
simple_request
).await?;
let complex_response = client.execute_chat_with_model(
Model::Claude3Opus20240229,
complex_request
).await?;
Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Links