1use serde::{Deserialize, Serialize};
4use std::time::Duration;
5use thiserror::Error;
6
7#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct TransportBufferLimits {
10 pub max_queued_input_bytes: usize,
12 pub max_queued_output_bytes: usize,
14 pub max_chunk_bytes: usize,
16}
17
18impl Default for TransportBufferLimits {
19 fn default() -> Self {
20 Self {
21 max_queued_input_bytes: 1024 * 1024,
22 max_queued_output_bytes: 1024 * 1024,
23 max_chunk_bytes: 256 * 1024,
24 }
25 }
26}
27
28#[derive(Clone, Debug)]
30pub struct ConnectorLimits {
31 pub connect_deadline: Duration,
33 pub buffers: TransportBufferLimits,
35 pub cancel_grace: Duration,
37 pub cleanup_deadline: Duration,
39}
40
41impl Default for ConnectorLimits {
42 fn default() -> Self {
43 Self {
44 connect_deadline: Duration::from_secs(30),
45 buffers: TransportBufferLimits::default(),
46 cancel_grace: Duration::from_secs(5),
47 cleanup_deadline: Duration::from_secs(10),
48 }
49 }
50}
51
52#[derive(Clone, Debug)]
54pub struct InterpretationLimits {
55 pub max_undecoded_bytes: usize,
57 pub max_frame_bytes: usize,
59 pub max_sentence_assembly_bytes: usize,
61 pub max_structural_atom_bytes: usize,
63 pub max_pending_tool_actions: usize,
65 pub max_bytes_per_tool_action: usize,
67 pub max_output_queue_items: usize,
69 pub max_safe_diagnostics: usize,
71}
72
73impl Default for InterpretationLimits {
74 fn default() -> Self {
75 Self {
76 max_undecoded_bytes: 4 * 1024 * 1024,
77 max_frame_bytes: 4 * 1024 * 1024,
78 max_sentence_assembly_bytes: 256 * 1024,
79 max_structural_atom_bytes: 512 * 1024,
80 max_pending_tool_actions: 256,
81 max_bytes_per_tool_action: 256 * 1024,
82 max_output_queue_items: 4096,
83 max_safe_diagnostics: 64,
84 }
85 }
86}
87
88#[derive(Clone, Debug, PartialEq, Eq)]
90pub struct InputLimits {
91 pub max_messages: usize,
93 pub max_content_parts: usize,
95 pub max_text_part_bytes: usize,
97 pub max_aggregate_text_bytes: usize,
99 pub max_tool_calls: usize,
101 pub max_tool_argument_bytes: usize,
103 pub max_json_depth: u32,
105 pub max_name_bytes: usize,
107 pub max_tool_call_id_bytes: usize,
109}
110
111impl Default for InputLimits {
112 fn default() -> Self {
113 Self {
114 max_messages: 256,
115 max_content_parts: 64,
116 max_text_part_bytes: 256 * 1024,
117 max_aggregate_text_bytes: 2 * 1024 * 1024,
118 max_tool_calls: 64,
119 max_tool_argument_bytes: 256 * 1024,
120 max_json_depth: 16,
121 max_name_bytes: 128,
122 max_tool_call_id_bytes: 128,
123 }
124 }
125}
126
127#[derive(Clone, Debug, PartialEq, Eq)]
129pub struct ExtensionLimits {
130 pub max_keys: usize,
132 pub max_key_bytes: usize,
134 pub max_value_depth: u32,
136 pub max_serialized_bytes: usize,
138}
139
140impl Default for ExtensionLimits {
141 fn default() -> Self {
142 Self {
143 max_keys: 32,
144 max_key_bytes: 64,
145 max_value_depth: 8,
146 max_serialized_bytes: 16 * 1024,
147 }
148 }
149}
150
151#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
153pub struct ToolLimits {
154 pub max_concurrent: usize,
156 pub max_input_bytes: usize,
158 pub max_output_bytes: usize,
160 #[serde(with = "duration_secs")]
162 pub execution_deadline: Duration,
163}
164
165impl Default for ToolLimits {
166 fn default() -> Self {
167 Self {
168 max_concurrent: 8,
169 max_input_bytes: 256 * 1024,
170 max_output_bytes: 256 * 1024,
171 execution_deadline: Duration::from_secs(60),
172 }
173 }
174}
175
176#[derive(Clone, Debug, PartialEq, Eq)]
178pub struct ChannelLimits {
179 pub max_active_transactions: usize,
181 pub max_distinct_sessions: usize,
183 pub max_encoded_exchange_bytes: usize,
185}
186
187impl Default for ChannelLimits {
188 fn default() -> Self {
189 Self {
190 max_active_transactions: 64,
191 max_distinct_sessions: 64,
192 max_encoded_exchange_bytes: 4 * 1024 * 1024,
193 }
194 }
195}
196
197#[derive(Clone, Debug, PartialEq, Eq)]
199pub struct TransactionLimits {
200 pub max_active_transactions: usize,
202 pub max_active_per_channel: usize,
204 pub max_actor_commands: usize,
206 pub max_actor_command_bytes: usize,
208 pub max_event_queue: usize,
211 pub max_event_queue_bytes: usize,
214 pub max_input_bytes: usize,
216 pub max_messages: usize,
218 pub max_content_parts: usize,
220 pub max_tools_per_transaction: usize,
222 pub max_tool_schema_bytes: usize,
224 pub max_tool_payload_bytes: usize,
226 pub max_tool_output_bytes: usize,
228 pub max_concurrent_tools_per_transaction: usize,
230 pub max_queued_tools_per_transaction: usize,
232 pub max_continuations: usize,
234 pub max_provider_exchanges: usize,
236 pub max_continuation_context_bytes: usize,
238 pub max_total_provider_input_bytes: usize,
240 pub max_total_provider_output_bytes: usize,
242 pub max_diagnostic_count: usize,
244 pub max_diagnostic_bytes: usize,
246 pub transaction_deadline: Duration,
248 pub cleanup_deadline: Duration,
250 pub terminal_event_delivery_deadline: Duration,
252 pub callback_deadline: Duration,
254}
255
256impl Default for TransactionLimits {
257 fn default() -> Self {
258 Self {
259 max_active_transactions: 256,
260 max_active_per_channel: 64,
261 max_actor_commands: 256,
262 max_actor_command_bytes: 1024 * 1024,
263 max_event_queue: 1024,
264 max_event_queue_bytes: 4 * 1024 * 1024,
265 max_input_bytes: 2 * 1024 * 1024,
266 max_messages: 256,
267 max_content_parts: 64,
268 max_tools_per_transaction: 64,
269 max_tool_schema_bytes: 64 * 1024,
270 max_tool_payload_bytes: 256 * 1024,
271 max_tool_output_bytes: 256 * 1024,
272 max_concurrent_tools_per_transaction: 16,
273 max_queued_tools_per_transaction: 64,
274 max_continuations: 32,
275 max_provider_exchanges: 64,
276 max_continuation_context_bytes: 2 * 1024 * 1024,
277 max_total_provider_input_bytes: 16 * 1024 * 1024,
278 max_total_provider_output_bytes: 16 * 1024 * 1024,
279 max_diagnostic_count: 64,
280 max_diagnostic_bytes: 1024,
281 transaction_deadline: Duration::from_secs(600),
282 cleanup_deadline: Duration::from_secs(30),
283 terminal_event_delivery_deadline: Duration::from_secs(10),
284 callback_deadline: Duration::from_secs(5),
285 }
286 }
287}
288
289#[derive(Clone, Debug, Error, PartialEq, Eq)]
291pub enum LimitsError {
292 #[error("limit must be non-zero: {0}")]
294 ZeroCapacity(&'static str),
295 #[error("inconsistent limits: {0}")]
297 Inconsistent(&'static str),
298}
299
300mod duration_secs {
301 use serde::{Deserialize, Deserializer, Serialize, Serializer};
302 use std::time::Duration;
303
304 pub fn serialize<S>(d: &Duration, serializer: S) -> Result<S::Ok, S::Error>
305 where
306 S: Serializer,
307 {
308 d.as_secs().serialize(serializer)
309 }
310
311 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
312 where
313 D: Deserializer<'de>,
314 {
315 let secs = u64::deserialize(deserializer)?;
316 Ok(Duration::from_secs(secs))
317 }
318}
319
320impl TransactionLimits {
321 pub fn validate(&self) -> Result<(), LimitsError> {
323 for (name, v) in [
324 ("max_active_transactions", self.max_active_transactions),
325 ("max_active_per_channel", self.max_active_per_channel),
326 ("max_actor_commands", self.max_actor_commands),
327 ("max_actor_command_bytes", self.max_actor_command_bytes),
328 ("max_event_queue", self.max_event_queue),
329 ("max_event_queue_bytes", self.max_event_queue_bytes),
330 ("max_input_bytes", self.max_input_bytes),
331 ("max_messages", self.max_messages),
332 ("max_content_parts", self.max_content_parts),
333 ("max_tools_per_transaction", self.max_tools_per_transaction),
334 ("max_tool_schema_bytes", self.max_tool_schema_bytes),
335 ("max_tool_payload_bytes", self.max_tool_payload_bytes),
336 ("max_tool_output_bytes", self.max_tool_output_bytes),
337 (
338 "max_concurrent_tools_per_transaction",
339 self.max_concurrent_tools_per_transaction,
340 ),
341 (
342 "max_queued_tools_per_transaction",
343 self.max_queued_tools_per_transaction,
344 ),
345 ("max_continuations", self.max_continuations.max(1)), ("max_provider_exchanges", self.max_provider_exchanges),
347 (
348 "max_continuation_context_bytes",
349 self.max_continuation_context_bytes,
350 ),
351 (
352 "max_total_provider_input_bytes",
353 self.max_total_provider_input_bytes,
354 ),
355 (
356 "max_total_provider_output_bytes",
357 self.max_total_provider_output_bytes,
358 ),
359 ("max_diagnostic_count", self.max_diagnostic_count),
360 ("max_diagnostic_bytes", self.max_diagnostic_bytes),
361 ] {
362 if name == "max_continuations" {
364 continue;
365 }
366 if v == 0 {
367 return Err(LimitsError::ZeroCapacity(name));
368 }
369 }
370 if self.callback_deadline.is_zero() {
371 return Err(LimitsError::ZeroCapacity("callback_deadline"));
372 }
373 if self.cleanup_deadline.is_zero() {
374 return Err(LimitsError::ZeroCapacity("cleanup_deadline"));
375 }
376 if self.terminal_event_delivery_deadline.is_zero() {
377 return Err(LimitsError::ZeroCapacity(
378 "terminal_event_delivery_deadline",
379 ));
380 }
381 if self.transaction_deadline.is_zero() {
382 return Err(LimitsError::ZeroCapacity("transaction_deadline"));
383 }
384 if self.max_active_per_channel > self.max_active_transactions {
385 return Err(LimitsError::Inconsistent(
386 "max_active_per_channel exceeds max_active_transactions",
387 ));
388 }
389 if self.max_event_queue_bytes < self.max_event_queue {
390 return Err(LimitsError::Inconsistent(
391 "max_event_queue_bytes smaller than max_event_queue items",
392 ));
393 }
394 Ok(())
395 }
396}