Skip to main content

systemprompt_api/services/gateway/protocol/outbound/anthropic/
mod.rs

1use anyhow::{Result, anyhow};
2use async_trait::async_trait;
3use serde_json::Value;
4
5use super::super::canonical_response::CanonicalResponse;
6use super::{OutboundAdapter, OutboundCtx, OutboundOutcome};
7
8mod request;
9mod response;
10mod streaming;
11
12#[derive(Debug, Clone, Copy, Default)]
13pub struct AnthropicOutbound;
14
15#[async_trait]
16impl OutboundAdapter for AnthropicOutbound {
17    fn provider_tag(&self) -> &'static str {
18        "anthropic"
19    }
20
21    async fn send(&self, ctx: OutboundCtx<'_>) -> Result<OutboundOutcome> {
22        let body = request::build_request_body(ctx.request, ctx.upstream_model);
23        let url = format!("{}/messages", ctx.route.endpoint.trim_end_matches('/'));
24
25        let client = reqwest::Client::new();
26        let mut req = client
27            .post(&url)
28            .header("x-api-key", ctx.api_key)
29            .header("anthropic-version", "2023-06-01")
30            .header("content-type", "application/json")
31            .json(&body);
32        for (name, value) in &ctx.route.extra_headers {
33            req = req.header(name.as_str(), value.as_str());
34        }
35        let upstream_response = req
36            .send()
37            .await
38            .map_err(|e| anyhow!("Upstream Anthropic request failed: {e}"))?;
39
40        let status = upstream_response.status();
41
42        if ctx.request.stream {
43            if !status.is_success() {
44                let err = upstream_response.text().await.unwrap_or_default();
45                return Err(anyhow!("Upstream error {status}: {err}"));
46            }
47            let stream = upstream_response.bytes_stream();
48            let event_stream = streaming::sse_to_canonical_events(stream);
49            return Ok(OutboundOutcome::Streaming(event_stream));
50        }
51
52        if !status.is_success() {
53            let err = upstream_response.text().await.unwrap_or_default();
54            return Err(anyhow!("Upstream error {status}: {err}"));
55        }
56
57        let bytes = upstream_response
58            .bytes()
59            .await
60            .map_err(|e| anyhow!("Failed to read Anthropic response: {e}"))?;
61        let value: Value = serde_json::from_slice(&bytes)
62            .map_err(|e| anyhow!("Anthropic response not valid JSON: {e}"))?;
63        let canon: CanonicalResponse = response::parse_response(&value, ctx.request.model.as_str());
64        Ok(OutboundOutcome::Buffered(canon))
65    }
66}