pub mod body;
pub mod content;
pub mod mcp;
pub mod message;
pub mod model;
pub mod role;
use crate::common::errors::{AnthropicToolError, Result};
use crate::messages::response::Response;
use std::env;
use body::{Body, Metadata, ThinkingConfig, ToolChoice};
use content::MediaType;
use message::{Message, SystemPrompt};
use model::Model;
const MESSAGES_API_URL: &str = "https://api.anthropic.com/v1/messages";
const ANTHROPIC_VERSION: &str = "2023-06-01";
#[derive(Debug, Clone)]
pub struct Messages {
api_key: String,
request_body: Body,
}
impl Default for Messages {
fn default() -> Self {
Self::new()
}
}
impl Messages {
pub fn new() -> Self {
let _ = dotenvy::dotenv();
let api_key = env::var("ANTHROPIC_API_KEY").unwrap_or_default();
Messages {
api_key,
request_body: Body::default(),
}
}
pub fn with_api_key<T: AsRef<str>>(api_key: T) -> Self {
Messages {
api_key: api_key.as_ref().to_string(),
request_body: Body::default(),
}
}
pub fn model<T: Into<Model>>(&mut self, model: T) -> &mut Self {
self.request_body.model = model.into();
self
}
pub fn max_tokens(&mut self, max_tokens: usize) -> &mut Self {
self.request_body.max_tokens = max_tokens;
self
}
pub fn system<T: AsRef<str>>(&mut self, system: T) -> &mut Self {
self.request_body.system = Some(SystemPrompt::text(system));
self
}
pub fn system_with_cache<T: AsRef<str>>(&mut self, system: T) -> &mut Self {
self.request_body.system = Some(SystemPrompt::with_cache(system));
self
}
pub fn messages(&mut self, messages: Vec<Message>) -> &mut Self {
self.request_body.messages = messages;
self
}
pub fn add_message(&mut self, message: Message) -> &mut Self {
self.request_body.messages.push(message);
self
}
pub fn user<T: AsRef<str>>(&mut self, text: T) -> &mut Self {
self.request_body.messages.push(Message::user(text));
self
}
pub fn assistant<T: AsRef<str>>(&mut self, text: T) -> &mut Self {
self.request_body.messages.push(Message::assistant(text));
self
}
pub fn user_with_image<T: AsRef<str>>(
&mut self,
text: T,
media_type: MediaType,
image_path: T,
) -> &mut Self {
self.request_body
.messages
.push(Message::user_with_image(text, media_type, image_path));
self
}
pub fn user_with_image_url<T: AsRef<str>>(&mut self, text: T, image_url: T) -> &mut Self {
self.request_body
.messages
.push(Message::user_with_image_url(text, image_url));
self
}
pub fn tool_result<S: AsRef<str>>(&mut self, tool_use_id: S, result_text: S) -> &mut Self {
self.request_body
.messages
.push(Message::tool_result(tool_use_id, result_text));
self
}
pub fn tool_error<S: AsRef<str>>(&mut self, tool_use_id: S, error_message: S) -> &mut Self {
self.request_body
.messages
.push(Message::tool_error(tool_use_id, error_message));
self
}
pub fn temperature(&mut self, temperature: f32) -> &mut Self {
self.request_body.temperature = Some(temperature);
self
}
pub fn top_p(&mut self, top_p: f32) -> &mut Self {
self.request_body.top_p = Some(top_p);
self
}
pub fn top_k(&mut self, top_k: u32) -> &mut Self {
self.request_body.top_k = Some(top_k);
self
}
pub fn stop_sequences(&mut self, sequences: Vec<String>) -> &mut Self {
self.request_body.stop_sequences = Some(sequences);
self
}
pub fn tools(&mut self, tools: Vec<serde_json::Value>) -> &mut Self {
self.request_body.tools = Some(tools);
self
}
pub fn tool_choice(&mut self, choice: ToolChoice) -> &mut Self {
self.request_body.tool_choice = Some(choice);
self
}
pub fn user_id<T: AsRef<str>>(&mut self, user_id: T) -> &mut Self {
self.request_body.metadata = Some(Metadata {
user_id: Some(user_id.as_ref().to_string()),
});
self
}
pub fn stream(&mut self, enabled: bool) -> &mut Self {
self.request_body.stream = Some(enabled);
self
}
pub fn container<T: AsRef<str>>(&mut self, container: T) -> &mut Self {
self.request_body.container = Some(container.as_ref().to_string());
self
}
pub fn thinking(&mut self, budget_tokens: usize) -> &mut Self {
self.request_body.thinking = Some(ThinkingConfig::enabled(budget_tokens));
self
}
fn build_headers(&self) -> request::header::HeaderMap {
let mut headers = request::header::HeaderMap::new();
headers.insert("x-api-key", self.api_key.parse().unwrap());
headers.insert("anthropic-version", ANTHROPIC_VERSION.parse().unwrap());
headers.insert("content-type", "application/json".parse().unwrap());
headers
}
pub async fn post(&self) -> Result<Response> {
if self.api_key.is_empty() {
return Err(AnthropicToolError::ApiKeyNotSet);
}
self.request_body.validate()?;
let client = request::Client::new();
let response = client
.post(MESSAGES_API_URL)
.headers(self.build_headers())
.json(&self.request_body)
.send()
.await?;
if response.status().is_success() {
let response_body: Response = response.json().await?;
Ok(response_body)
} else {
let error_response: crate::common::errors::ErrorResponse = response.json().await?;
Err(error_response.into_error())
}
}
pub fn body(&self) -> &Body {
&self.request_body
}
}