sentinel_agent_protocol/v2/
capabilities.rs1use serde::{Deserialize, Serialize};
4use crate::EventType;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct AgentCapabilities {
9 pub protocol_version: u32,
10 pub agent_id: String,
11 pub name: String,
12 pub version: String,
13 pub supported_events: Vec<EventType>,
14 #[serde(default)]
15 pub features: AgentFeatures,
16 #[serde(default)]
17 pub limits: AgentLimits,
18 #[serde(default)]
19 pub health: HealthConfig,
20}
21
22impl AgentCapabilities {
23 pub fn new(agent_id: impl Into<String>, name: impl Into<String>, version: impl Into<String>) -> Self {
24 Self {
25 protocol_version: super::PROTOCOL_VERSION_2,
26 agent_id: agent_id.into(),
27 name: name.into(),
28 version: version.into(),
29 supported_events: vec![EventType::RequestHeaders],
30 features: AgentFeatures::default(),
31 limits: AgentLimits::default(),
32 health: HealthConfig::default(),
33 }
34 }
35
36 pub fn supports_event(&self, event_type: EventType) -> bool {
37 self.supported_events.contains(&event_type)
38 }
39
40 pub fn with_event(mut self, event_type: EventType) -> Self {
41 if !self.supported_events.contains(&event_type) {
42 self.supported_events.push(event_type);
43 }
44 self
45 }
46
47 pub fn with_features(mut self, features: AgentFeatures) -> Self {
48 self.features = features;
49 self
50 }
51
52 pub fn with_limits(mut self, limits: AgentLimits) -> Self {
53 self.limits = limits;
54 self
55 }
56}
57
58#[derive(Debug, Clone, Default, Serialize, Deserialize)]
60pub struct AgentFeatures {
61 #[serde(default)]
62 pub streaming_body: bool,
63 #[serde(default)]
64 pub websocket: bool,
65 #[serde(default)]
66 pub guardrails: bool,
67 #[serde(default)]
68 pub config_push: bool,
69 #[serde(default)]
70 pub metrics_export: bool,
71 #[serde(default)]
72 pub concurrent_requests: u32,
73 #[serde(default)]
74 pub cancellation: bool,
75 #[serde(default)]
76 pub flow_control: bool,
77 #[serde(default)]
78 pub health_reporting: bool,
79}
80
81impl AgentFeatures {
82 pub fn simple() -> Self { Self::default() }
83 pub fn full() -> Self {
84 Self {
85 streaming_body: true,
86 websocket: true,
87 guardrails: true,
88 config_push: true,
89 metrics_export: true,
90 concurrent_requests: 100,
91 cancellation: true,
92 flow_control: true,
93 health_reporting: true,
94 }
95 }
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct AgentLimits {
101 pub max_body_size: usize,
102 pub max_concurrency: u32,
103 pub preferred_chunk_size: usize,
104 pub max_memory: Option<usize>,
105 pub max_processing_time_ms: Option<u64>,
106}
107
108impl Default for AgentLimits {
109 fn default() -> Self {
110 Self {
111 max_body_size: 10 * 1024 * 1024,
112 max_concurrency: 100,
113 preferred_chunk_size: 64 * 1024,
114 max_memory: None,
115 max_processing_time_ms: Some(5000),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct HealthConfig {
123 pub report_interval_ms: u32,
124 pub include_load_metrics: bool,
125 pub include_resource_metrics: bool,
126}
127
128impl Default for HealthConfig {
129 fn default() -> Self {
130 Self {
131 report_interval_ms: 10_000,
132 include_load_metrics: true,
133 include_resource_metrics: false,
134 }
135 }
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
140pub struct HandshakeRequest {
141 pub supported_versions: Vec<u32>,
142 pub proxy_id: String,
143 pub proxy_version: String,
144 pub config: serde_json::Value,
145}
146
147impl HandshakeRequest {
148 pub fn new(proxy_id: impl Into<String>, proxy_version: impl Into<String>) -> Self {
149 Self {
150 supported_versions: vec![super::PROTOCOL_VERSION_2, 1],
151 proxy_id: proxy_id.into(),
152 proxy_version: proxy_version.into(),
153 config: serde_json::Value::Null,
154 }
155 }
156
157 pub fn max_version(&self) -> u32 {
158 self.supported_versions.first().copied().unwrap_or(1)
159 }
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct HandshakeResponse {
165 pub protocol_version: u32,
166 pub capabilities: AgentCapabilities,
167 pub success: bool,
168 pub error: Option<String>,
169}
170
171impl HandshakeResponse {
172 pub fn success(capabilities: AgentCapabilities) -> Self {
173 Self {
174 protocol_version: capabilities.protocol_version,
175 capabilities,
176 success: true,
177 error: None,
178 }
179 }
180
181 pub fn failure(error: impl Into<String>) -> Self {
182 Self {
183 protocol_version: 0,
184 capabilities: AgentCapabilities::new("", "", ""),
185 success: false,
186 error: Some(error.into()),
187 }
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_capabilities_builder() {
197 let caps = AgentCapabilities::new("test-agent", "Test Agent", "1.0.0")
198 .with_event(EventType::RequestHeaders)
199 .with_features(AgentFeatures::full());
200
201 assert_eq!(caps.agent_id, "test-agent");
202 assert!(caps.supports_event(EventType::RequestHeaders));
203 assert!(caps.features.streaming_body);
204 }
205
206 #[test]
207 fn test_handshake() {
208 let request = HandshakeRequest::new("proxy-1", "0.2.5");
209 assert_eq!(request.max_version(), 2);
210
211 let caps = AgentCapabilities::new("agent-1", "My Agent", "1.0.0");
212 let response = HandshakeResponse::success(caps);
213 assert!(response.success);
214 }
215
216 #[test]
217 fn test_features() {
218 let simple = AgentFeatures::simple();
219 assert!(!simple.streaming_body);
220
221 let full = AgentFeatures::full();
222 assert!(full.streaming_body);
223 }
224}