Skip to main content

systemprompt_api/services/gateway/protocol/inbound/anthropic_messages/
mod.rs

1//! Inbound adapter for the Anthropic Messages wire protocol.
2//!
3//! [`AnthropicMessagesInbound`] parses Messages-format request bodies into the
4//! canonical request model and renders canonical responses, streaming events,
5//! and errors back in Messages format.
6//!
7//! Copyright (c) systemprompt.io — Business Source License 1.1.
8//! See <https://systemprompt.io> for licensing details.
9
10use bytes::Bytes;
11use http::StatusCode;
12use serde_json::Value;
13
14use super::super::canonical::CanonicalRequest;
15use super::super::canonical_response::{CanonicalEvent, CanonicalResponse};
16use super::{InboundAdapter, InboundParseError};
17
18mod parse;
19mod render;
20
21pub use systemprompt_models::wire::anthropic::content_to_anthropic_block;
22
23#[cfg(feature = "test-api")]
24pub mod test_api {
25    pub use super::parse::parse as parse_request;
26    pub use super::render::{render_event_frame, render_response_value};
27}
28
29#[derive(Debug, Clone, Copy, Default)]
30pub struct AnthropicMessagesInbound;
31
32impl InboundAdapter for AnthropicMessagesInbound {
33    fn wire_name(&self) -> &'static str {
34        "anthropic.messages"
35    }
36
37    fn parse_request(&self, raw: &Bytes) -> Result<CanonicalRequest, InboundParseError> {
38        let value: Value = serde_json::from_slice(raw)
39            .map_err(|e| InboundParseError::InvalidJson(e.to_string()))?;
40        parse::parse(&value)
41    }
42
43    fn render_response(&self, response: &CanonicalResponse) -> Bytes {
44        let value = render::render_response_value(response);
45        Bytes::from(serde_json::to_vec(&value).unwrap_or_else(|_| b"{}".to_vec()))
46    }
47
48    fn render_event(&self, event: &CanonicalEvent, model: &str) -> Option<Bytes> {
49        render::render_event_frame(event, model)
50    }
51
52    fn render_error(&self, _status: StatusCode, message: &str) -> Bytes {
53        let escaped = message.replace('\\', "\\\\").replace('"', "\\\"");
54        let body = format!(
55            "{{\"type\":\"error\",\"error\":{{\"type\":\"api_error\",\"message\":\"{escaped}\"}}}}"
56        );
57        Bytes::from(body)
58    }
59}