Skip to main content

split_brain_harness/backends/
anthropic.rs

1use 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}