split_brain_harness/backends/
anthropic.rs1use super::InferenceEngine;
2use async_trait::async_trait;
3use reqwest::Client;
4use serde_json::json;
5
6pub struct AnthropicEngine {
7 pub endpoint: String,
8 pub model: String,
9 pub api_key: String,
10 pub client: Client,
11}
12
13#[async_trait]
14impl InferenceEngine for AnthropicEngine {
15 async fn generate(&self, system_prompt: &str, prompt_payload: &str) -> Result<String, String> {
16 if self.api_key.is_empty() {
17 return Err("SBH_API_KEY is required for the Anthropic backend".into());
18 }
19
20 let body = json!({
21 "model": self.model,
22 "max_tokens": 2048,
23 "system": system_prompt,
24 "messages": [
25 { "role": "user", "content": prompt_payload }
26 ]
27 });
28
29 let resp = self
30 .client
31 .post(format!(
32 "{}/v1/messages",
33 self.endpoint.trim_end_matches('/')
34 ))
35 .header("x-api-key", &self.api_key)
36 .header("anthropic-version", "2023-06-01")
37 .header("content-type", "application/json")
38 .json(&body)
39 .send()
40 .await
41 .map_err(|e| e.to_string())?;
42
43 let status = resp.status();
44 let json: serde_json::Value = resp.json().await.map_err(|e| e.to_string())?;
45
46 if !status.is_success() {
47 let msg = json["error"]["message"].as_str().unwrap_or("unknown error");
48 return Err(format!("Anthropic API error {status}: {msg}"));
49 }
50
51 json["content"][0]["text"]
52 .as_str()
53 .map(|s| s.to_string())
54 .ok_or_else(|| "missing content[0].text in Anthropic response".into())
55 }
56}