agentik_sdk/client.rs
1use crate::config::ClientConfig;
2use crate::http::HttpClient;
3use crate::resources::{BatchesResource, FilesResource, MessagesResource, ModelsResource};
4use crate::types::errors::Result;
5use crate::utils::init_logging;
6
7/// Main Anthropic API client
8pub struct Anthropic {
9 config: ClientConfig,
10 http_client: HttpClient,
11}
12
13impl Anthropic {
14 /// Create a new Anthropic client with the provided API key
15 pub fn new(api_key: impl Into<String>) -> Result<Self> {
16 let config = ClientConfig::new(api_key);
17 let http_client = HttpClient::new(config.clone())?;
18
19 // Initialize logging
20 init_logging(&config.log_level);
21
22 Ok(Self {
23 config,
24 http_client,
25 })
26 }
27
28 /// Create a new Anthropic client from environment variables
29 pub fn from_env() -> Result<Self> {
30 let config = ClientConfig::from_env()?;
31 let http_client = HttpClient::new(config.clone())?;
32
33 // Initialize logging
34 init_logging(&config.log_level);
35
36 Ok(Self {
37 config,
38 http_client,
39 })
40 }
41
42 /// Create a new Anthropic client with custom configuration
43 pub fn with_config(config: ClientConfig) -> Result<Self> {
44 let http_client = HttpClient::new(config.clone())?;
45
46 // Initialize logging
47 init_logging(&config.log_level);
48
49 Ok(Self {
50 config,
51 http_client,
52 })
53 }
54
55 /// Get the current configuration
56 pub fn config(&self) -> &ClientConfig {
57 &self.config
58 }
59
60 /// Get a reference to the HTTP client for internal use
61 pub(crate) fn http_client(&self) -> &HttpClient {
62 &self.http_client
63 }
64
65 /// Test the connection by making a simple API call
66 pub async fn test_connection(&self) -> Result<()> {
67 // This will be implemented once we have the messages API
68 // For now, we'll just validate the configuration
69 self.config.validate()?;
70 tracing::info!("Anthropic client initialized successfully");
71 Ok(())
72 }
73
74 /// Access to the Messages API
75 ///
76 /// # Example
77 ///
78 /// ```rust,no_run
79 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
80 /// use agentik_sdk::{Anthropic, types::MessageCreateBuilder};
81 ///
82 /// let client = Anthropic::from_env()?;
83 ///
84 /// let message = client.messages().create(
85 /// MessageCreateBuilder::new("claude-3-5-sonnet-latest", 1024)
86 /// .user("Hello, Claude!")
87 /// .build()
88 /// ).await?;
89 ///
90 /// println!("Claude responded: {:?}", message.content);
91 /// # Ok(())
92 /// # }
93 /// ```
94 pub fn messages(&self) -> MessagesResource<'_> {
95 MessagesResource::new(self)
96 }
97
98 /// Access to the Message Batches API (Beta)
99 ///
100 /// # Example
101 ///
102 /// ```rust,no_run
103 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
104 /// use agentik_sdk::{Anthropic, types::{BatchRequest, BatchCreateParams}};
105 ///
106 /// let client = Anthropic::from_env()?;
107 ///
108 /// let requests = vec![
109 /// BatchRequest::new("req1", "claude-3-5-sonnet-latest", 1024)
110 /// .user("Hello, world!")
111 /// .build(),
112 /// ];
113 ///
114 /// let batch = client.batches().create(
115 /// BatchCreateParams::new(requests)
116 /// ).await?;
117 ///
118 /// println!("Created batch: {}", batch.id);
119 /// # Ok(())
120 /// # }
121 /// ```
122 pub fn batches(&self) -> BatchesResource {
123 BatchesResource::new(std::sync::Arc::new(self.http_client.clone()))
124 }
125
126 /// Access to the Files API (Beta)
127 ///
128 /// # Example
129 ///
130 /// ```rust,no_run
131 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
132 /// use agentik_sdk::{Anthropic, FileUploadParams, FilePurpose};
133 ///
134 /// let client = Anthropic::from_env()?;
135 ///
136 /// let upload_params = FileUploadParams::new(
137 /// std::fs::read("document.pdf")?,
138 /// "document.pdf",
139 /// "application/pdf",
140 /// FilePurpose::Document,
141 /// );
142 ///
143 /// let file_object = client.files().upload(upload_params).await?;
144 /// println!("Uploaded file: {}", file_object.id);
145 /// # Ok(())
146 /// # }
147 /// ```
148 pub fn files(&self) -> FilesResource {
149 FilesResource::new(std::sync::Arc::new(self.http_client.clone()))
150 }
151
152 /// Access to the Models API
153 ///
154 /// # Example
155 ///
156 /// ```rust,no_run
157 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
158 /// use agentik_sdk::{Anthropic, ModelListParams, ModelRequirements, ModelCapability};
159 ///
160 /// let client = Anthropic::from_env()?;
161 ///
162 /// // List all models
163 /// let models = client.models().list(None).await?;
164 /// println!("Found {} models", models.data.len());
165 ///
166 /// // Get specific model
167 /// let model = client.models().get("claude-3-5-sonnet-latest").await?;
168 /// println!("Model: {} ({})", model.display_name, model.id);
169 ///
170 /// // Find best model for requirements
171 /// let requirements = ModelRequirements::new()
172 /// .require_vision()
173 /// .min_context_length(100000);
174 /// let best_model = client.models().find_best_model(&requirements).await?;
175 ///
176 /// // Compare models
177 /// let comparison = client.models().compare_models(&[
178 /// "claude-3-5-sonnet-latest",
179 /// "claude-3-5-haiku-latest"
180 /// ]).await?;
181 ///
182 /// // Estimate costs
183 /// let cost = client.models().estimate_cost("claude-3-5-sonnet-latest", 1000, 500).await?;
184 /// println!("Estimated cost: ${:.4}", cost.final_cost_usd);
185 /// # Ok(())
186 /// # }
187 /// ```
188 pub fn models(&self) -> ModelsResource<'_> {
189 ModelsResource::new(self)
190 }
191}
192
193impl std::fmt::Debug for Anthropic {
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 f.debug_struct("Anthropic")
196 .field("base_url", &self.config.base_url)
197 .field("timeout", &self.config.timeout)
198 .field("max_retries", &self.config.max_retries)
199 .field("log_level", &self.config.log_level)
200 .finish_non_exhaustive()
201 }
202}
203