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,
210 pub max_event_queue_bytes: usize,
212 pub max_input_bytes: usize,
214 pub max_messages: usize,
216 pub max_content_parts: usize,
218 pub max_tools_per_transaction: usize,
220 pub max_tool_schema_bytes: usize,
222 pub max_tool_payload_bytes: usize,
224 pub max_tool_output_bytes: usize,
226 pub max_concurrent_tools_per_transaction: usize,
228 pub max_queued_tools_per_transaction: usize,
230 pub max_continuations: usize,
232 pub max_provider_exchanges: usize,
234 pub max_continuation_context_bytes: usize,
236 pub max_total_provider_input_bytes: usize,
238 pub max_total_provider_output_bytes: usize,
240 pub max_diagnostic_count: usize,
242 pub max_diagnostic_bytes: usize,
244 pub transaction_deadline: Duration,
246 pub cleanup_deadline: Duration,
248 pub terminal_event_delivery_deadline: Duration,
250 pub callback_deadline: Duration,
252}
253
254impl Default for TransactionLimits {
255 fn default() -> Self {
256 Self {
257 max_active_transactions: 256,
258 max_active_per_channel: 64,
259 max_actor_commands: 256,
260 max_actor_command_bytes: 1024 * 1024,
261 max_event_queue: 1024,
262 max_event_queue_bytes: 4 * 1024 * 1024,
263 max_input_bytes: 2 * 1024 * 1024,
264 max_messages: 256,
265 max_content_parts: 64,
266 max_tools_per_transaction: 64,
267 max_tool_schema_bytes: 64 * 1024,
268 max_tool_payload_bytes: 256 * 1024,
269 max_tool_output_bytes: 256 * 1024,
270 max_concurrent_tools_per_transaction: 16,
271 max_queued_tools_per_transaction: 64,
272 max_continuations: 32,
273 max_provider_exchanges: 64,
274 max_continuation_context_bytes: 2 * 1024 * 1024,
275 max_total_provider_input_bytes: 16 * 1024 * 1024,
276 max_total_provider_output_bytes: 16 * 1024 * 1024,
277 max_diagnostic_count: 64,
278 max_diagnostic_bytes: 1024,
279 transaction_deadline: Duration::from_secs(600),
280 cleanup_deadline: Duration::from_secs(30),
281 terminal_event_delivery_deadline: Duration::from_secs(10),
282 callback_deadline: Duration::from_secs(5),
283 }
284 }
285}
286
287#[derive(Clone, Debug, Error, PartialEq, Eq)]
289pub enum LimitsError {
290 #[error("limit must be non-zero: {0}")]
292 ZeroCapacity(&'static str),
293 #[error("inconsistent limits: {0}")]
295 Inconsistent(&'static str),
296}
297
298mod duration_secs {
299 use serde::{Deserialize, Deserializer, Serialize, Serializer};
300 use std::time::Duration;
301
302 pub fn serialize<S>(d: &Duration, serializer: S) -> Result<S::Ok, S::Error>
303 where
304 S: Serializer,
305 {
306 d.as_secs().serialize(serializer)
307 }
308
309 pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
310 where
311 D: Deserializer<'de>,
312 {
313 let secs = u64::deserialize(deserializer)?;
314 Ok(Duration::from_secs(secs))
315 }
316}
317
318impl TransactionLimits {
319 pub fn validate(&self) -> Result<(), LimitsError> {
321 for (name, v) in [
322 ("max_active_transactions", self.max_active_transactions),
323 ("max_active_per_channel", self.max_active_per_channel),
324 ("max_actor_commands", self.max_actor_commands),
325 ("max_actor_command_bytes", self.max_actor_command_bytes),
326 ("max_event_queue", self.max_event_queue),
327 ("max_event_queue_bytes", self.max_event_queue_bytes),
328 ("max_input_bytes", self.max_input_bytes),
329 ("max_messages", self.max_messages),
330 ("max_content_parts", self.max_content_parts),
331 ("max_tools_per_transaction", self.max_tools_per_transaction),
332 ("max_tool_schema_bytes", self.max_tool_schema_bytes),
333 ("max_tool_payload_bytes", self.max_tool_payload_bytes),
334 ("max_tool_output_bytes", self.max_tool_output_bytes),
335 (
336 "max_concurrent_tools_per_transaction",
337 self.max_concurrent_tools_per_transaction,
338 ),
339 (
340 "max_queued_tools_per_transaction",
341 self.max_queued_tools_per_transaction,
342 ),
343 ("max_continuations", self.max_continuations.max(1)), ("max_provider_exchanges", self.max_provider_exchanges),
345 (
346 "max_continuation_context_bytes",
347 self.max_continuation_context_bytes,
348 ),
349 (
350 "max_total_provider_input_bytes",
351 self.max_total_provider_input_bytes,
352 ),
353 (
354 "max_total_provider_output_bytes",
355 self.max_total_provider_output_bytes,
356 ),
357 ("max_diagnostic_count", self.max_diagnostic_count),
358 ("max_diagnostic_bytes", self.max_diagnostic_bytes),
359 ] {
360 if name == "max_continuations" {
362 continue;
363 }
364 if v == 0 {
365 return Err(LimitsError::ZeroCapacity(name));
366 }
367 }
368 if self.callback_deadline.is_zero() {
369 return Err(LimitsError::ZeroCapacity("callback_deadline"));
370 }
371 if self.cleanup_deadline.is_zero() {
372 return Err(LimitsError::ZeroCapacity("cleanup_deadline"));
373 }
374 if self.terminal_event_delivery_deadline.is_zero() {
375 return Err(LimitsError::ZeroCapacity(
376 "terminal_event_delivery_deadline",
377 ));
378 }
379 if self.transaction_deadline.is_zero() {
380 return Err(LimitsError::ZeroCapacity("transaction_deadline"));
381 }
382 if self.max_active_per_channel > self.max_active_transactions {
383 return Err(LimitsError::Inconsistent(
384 "max_active_per_channel exceeds max_active_transactions",
385 ));
386 }
387 if self.max_event_queue_bytes < self.max_event_queue {
388 return Err(LimitsError::Inconsistent(
389 "max_event_queue_bytes smaller than max_event_queue items",
390 ));
391 }
392 Ok(())
393 }
394}