1pub mod claude {
2 use reqwest::header::{HeaderMap, HeaderValue, ACCEPT, CONTENT_TYPE};
3 use serde::{Deserialize, Serialize};
4 use std::env;
5
6 const ANTHROPIC_API_URL: &str = "https://api.anthropic.com/v1";
7 const DEFAULT_MODEL: &str = "claude-3-7-sonnet-20250219";
8
9 #[derive(Debug, Serialize)]
10 pub struct Message {
11 pub role: String,
12 pub content: String,
13 }
14
15 #[derive(Debug, Serialize)]
16 pub struct ClaudeRequest {
17 pub model: String,
18 pub messages: Vec<Message>,
19 pub max_tokens: u32,
20 pub system: Option<String>,
21 }
22
23 #[derive(Debug, Deserialize)]
24 pub struct ClaudeResponse {
25 pub content: Vec<Content>,
26 pub role: String,
27 pub model: String,
28 pub id: String,
29 }
30
31 #[derive(Debug, Deserialize)]
32 pub struct Content {
33 pub text: String,
34 #[serde(rename = "type")]
35 pub content_type: String,
36 }
37
38 #[derive(Debug, Deserialize)]
39 pub struct Model {
40 #[serde(rename = "type")]
41 pub model_type: String,
42 pub id: String,
43 pub display_name: String,
44 pub created_at: String,
45 }
46
47 #[derive(Debug, Deserialize)]
48 pub struct ListModelsResponse {
49 pub data: Vec<Model>,
50 pub has_more: bool,
51 pub first_id: String,
52 pub last_id: String,
53 }
54
55 pub struct ClaudeClient {
56 client: reqwest::Client,
57 api_key: String,
58 }
59
60 impl ClaudeClient {
61 pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
62 let api_key = env::var("ANTHROPIC_API_KEY")
63 .map_err(|_| "ANTHROPIC_API_KEY environment variable not set")?;
64
65 Ok(Self {
66 client: reqwest::Client::new(),
67 api_key,
68 })
69 }
70
71 pub async fn send_message(
72 &self,
73 model: Option<&str>,
74 system_prompt: &str,
75 user_message: &str,
76 ) -> Result<String, Box<dyn std::error::Error>> {
77 let model = model.unwrap_or(DEFAULT_MODEL);
78 let mut headers = HeaderMap::new();
79 headers.insert(
80 "x-api-key",
81 HeaderValue::from_str(&self.api_key)
82 .map_err(|e| format!("Invalid API key format: {}", e))?,
83 );
84 headers.insert(
85 "anthropic-api-key",
86 HeaderValue::from_str(&self.api_key)
87 .map_err(|e| format!("Invalid API key format: {}", e))?,
88 );
89 headers.insert(
90 "anthropic-version",
91 HeaderValue::from_static("2023-06-01"),
92 );
93 headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
94 headers.insert(
95 CONTENT_TYPE,
96 HeaderValue::from_static("application/json"),
97 );
98
99 let request = ClaudeRequest {
100 model: model.to_string(),
101 messages: vec![
102 Message {
103 role: "user".to_string(),
104 content: user_message.to_string(),
105 },
106 ],
107 max_tokens: 4000,
108 system: Some(system_prompt.to_string()),
109 };
110
111 let raw_response = self
112 .client
113 .post(format!("{}/messages", ANTHROPIC_API_URL))
114 .headers(headers)
115 .json(&request)
116 .send()
117 .await?
118 .text()
119 .await?;
120 println!("Raw response: {}", raw_response);
121 let response: ClaudeResponse = serde_json::from_str(&raw_response)?;
122
123 Ok(response.content.into_iter()
124 .filter(|c| c.content_type == "text")
125 .map(|c| c.text)
126 .collect::<Vec<_>>()
127 .join(""))
128 }
129
130 pub async fn list_models(&self) -> Result<Vec<Model>, Box<dyn std::error::Error>> {
131 let mut headers = HeaderMap::new();
132 headers.insert(
133 "x-api-key",
134 HeaderValue::from_str(&self.api_key)
135 .map_err(|e| format!("Invalid API key format: {}", e))?,
136 );
137 headers.insert(
138 "anthropic-api-key",
139 HeaderValue::from_str(&self.api_key)
140 .map_err(|e| format!("Invalid API key format: {}", e))?,
141 );
142 headers.insert(
143 "anthropic-version",
144 HeaderValue::from_static("2023-06-01"),
145 );
146 headers.insert(ACCEPT, HeaderValue::from_static("application/json"));
147
148 let raw_response = self
149 .client
150 .get(format!("{}/models", ANTHROPIC_API_URL))
151 .headers(headers)
152 .send()
153 .await?
154 .text()
155 .await?;
156 println!("Raw response: {}", raw_response);
157 let response: ListModelsResponse = serde_json::from_str(&raw_response)?;
158
159 Ok(response.data)
160 }
161 }
162}
163
164#[cfg(test)]
165mod tests;