systemprompt_api/services/gateway/protocol/inbound/
mod.rs1pub mod anthropic_messages;
13pub mod openai_chat;
14pub mod openai_responses;
15
16use bytes::Bytes;
17use http::StatusCode;
18use systemprompt_models::services::WireProtocol;
19use systemprompt_models::wire::origin::InboundWireProtocol;
20
21use super::canonical::CanonicalRequest;
22use super::canonical_response::{CanonicalEvent, CanonicalResponse};
23
24#[derive(Debug, thiserror::Error)]
25pub enum InboundParseError {
26 #[error("invalid request body: {0}")]
27 InvalidJson(String),
28 #[error("missing required field: {0}")]
29 MissingField(&'static str),
30 #[error("unsupported value for {field}: {detail}")]
31 Unsupported { field: &'static str, detail: String },
32}
33
34pub(crate) fn error_type_for_status(status: StatusCode) -> &'static str {
35 match status {
36 StatusCode::UNAUTHORIZED => "authentication_error",
37 StatusCode::FORBIDDEN => "permission_error",
38 StatusCode::NOT_FOUND => "not_found_error",
39 StatusCode::TOO_MANY_REQUESTS => "rate_limit_error",
40 s if s.is_client_error() => "invalid_request_error",
41 _ => "api_error",
42 }
43}
44
45pub trait InboundAdapter: Send + Sync + std::fmt::Debug {
46 fn wire(&self) -> InboundWireProtocol;
47
48 fn wire_name(&self) -> &'static str {
49 self.wire().as_str()
50 }
51
52 fn passthrough_wire(&self) -> Option<WireProtocol> {
53 None
54 }
55
56 fn parse_request(&self, raw: &Bytes) -> Result<CanonicalRequest, InboundParseError>;
57 fn render_response(&self, response: &CanonicalResponse) -> Bytes;
58 fn render_event(&self, event: &CanonicalEvent, model: &str) -> Option<Bytes>;
59
60 fn render_terminal_event(
61 &self,
62 _event: &CanonicalEvent,
63 _snapshot: &CanonicalResponse,
64 _model: &str,
65 ) -> Option<Bytes> {
66 None
67 }
68
69 fn wants_stream_usage(&self, _raw: &Bytes) -> bool {
70 false
71 }
72
73 fn render_stream_tail(
74 &self,
75 _snapshot: &CanonicalResponse,
76 _include_usage: bool,
77 ) -> Option<Bytes> {
78 None
79 }
80
81 fn render_error(&self, status: StatusCode, message: &str) -> Bytes;
82 fn streaming_content_type(&self) -> &'static str {
83 "text/event-stream"
84 }
85}