use serde::{Deserialize, Serialize};
use std::time::Duration;
use thiserror::Error;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransportBufferLimits {
pub max_queued_input_bytes: usize,
pub max_queued_output_bytes: usize,
pub max_chunk_bytes: usize,
}
impl Default for TransportBufferLimits {
fn default() -> Self {
Self {
max_queued_input_bytes: 1024 * 1024,
max_queued_output_bytes: 1024 * 1024,
max_chunk_bytes: 256 * 1024,
}
}
}
#[derive(Clone, Debug)]
pub struct ConnectorLimits {
pub connect_deadline: Duration,
pub buffers: TransportBufferLimits,
pub cancel_grace: Duration,
pub cleanup_deadline: Duration,
}
impl Default for ConnectorLimits {
fn default() -> Self {
Self {
connect_deadline: Duration::from_secs(30),
buffers: TransportBufferLimits::default(),
cancel_grace: Duration::from_secs(5),
cleanup_deadline: Duration::from_secs(10),
}
}
}
#[derive(Clone, Debug)]
pub struct InterpretationLimits {
pub max_undecoded_bytes: usize,
pub max_frame_bytes: usize,
pub max_sentence_assembly_bytes: usize,
pub max_structural_atom_bytes: usize,
pub max_pending_tool_actions: usize,
pub max_bytes_per_tool_action: usize,
pub max_output_queue_items: usize,
pub max_safe_diagnostics: usize,
}
impl Default for InterpretationLimits {
fn default() -> Self {
Self {
max_undecoded_bytes: 4 * 1024 * 1024,
max_frame_bytes: 4 * 1024 * 1024,
max_sentence_assembly_bytes: 256 * 1024,
max_structural_atom_bytes: 512 * 1024,
max_pending_tool_actions: 256,
max_bytes_per_tool_action: 256 * 1024,
max_output_queue_items: 4096,
max_safe_diagnostics: 64,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InputLimits {
pub max_messages: usize,
pub max_content_parts: usize,
pub max_text_part_bytes: usize,
pub max_aggregate_text_bytes: usize,
pub max_tool_calls: usize,
pub max_tool_argument_bytes: usize,
pub max_json_depth: u32,
pub max_name_bytes: usize,
pub max_tool_call_id_bytes: usize,
}
impl Default for InputLimits {
fn default() -> Self {
Self {
max_messages: 256,
max_content_parts: 64,
max_text_part_bytes: 256 * 1024,
max_aggregate_text_bytes: 2 * 1024 * 1024,
max_tool_calls: 64,
max_tool_argument_bytes: 256 * 1024,
max_json_depth: 16,
max_name_bytes: 128,
max_tool_call_id_bytes: 128,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExtensionLimits {
pub max_keys: usize,
pub max_key_bytes: usize,
pub max_value_depth: u32,
pub max_serialized_bytes: usize,
}
impl Default for ExtensionLimits {
fn default() -> Self {
Self {
max_keys: 32,
max_key_bytes: 64,
max_value_depth: 8,
max_serialized_bytes: 16 * 1024,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolLimits {
pub max_concurrent: usize,
pub max_input_bytes: usize,
pub max_output_bytes: usize,
#[serde(with = "duration_secs")]
pub execution_deadline: Duration,
}
impl Default for ToolLimits {
fn default() -> Self {
Self {
max_concurrent: 8,
max_input_bytes: 256 * 1024,
max_output_bytes: 256 * 1024,
execution_deadline: Duration::from_secs(60),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ChannelLimits {
pub max_active_transactions: usize,
pub max_distinct_sessions: usize,
pub max_encoded_exchange_bytes: usize,
}
impl Default for ChannelLimits {
fn default() -> Self {
Self {
max_active_transactions: 64,
max_distinct_sessions: 64,
max_encoded_exchange_bytes: 4 * 1024 * 1024,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransactionLimits {
pub max_active_transactions: usize,
pub max_active_per_channel: usize,
pub max_actor_commands: usize,
pub max_actor_command_bytes: usize,
pub max_event_queue: usize,
pub max_event_queue_bytes: usize,
pub max_input_bytes: usize,
pub max_messages: usize,
pub max_content_parts: usize,
pub max_tools_per_transaction: usize,
pub max_tool_schema_bytes: usize,
pub max_tool_payload_bytes: usize,
pub max_tool_output_bytes: usize,
pub max_concurrent_tools_per_transaction: usize,
pub max_queued_tools_per_transaction: usize,
pub max_continuations: usize,
pub max_provider_exchanges: usize,
pub max_continuation_context_bytes: usize,
pub max_total_provider_input_bytes: usize,
pub max_total_provider_output_bytes: usize,
pub max_diagnostic_count: usize,
pub max_diagnostic_bytes: usize,
pub transaction_deadline: Duration,
pub cleanup_deadline: Duration,
pub terminal_event_delivery_deadline: Duration,
pub callback_deadline: Duration,
}
impl Default for TransactionLimits {
fn default() -> Self {
Self {
max_active_transactions: 256,
max_active_per_channel: 64,
max_actor_commands: 256,
max_actor_command_bytes: 1024 * 1024,
max_event_queue: 1024,
max_event_queue_bytes: 4 * 1024 * 1024,
max_input_bytes: 2 * 1024 * 1024,
max_messages: 256,
max_content_parts: 64,
max_tools_per_transaction: 64,
max_tool_schema_bytes: 64 * 1024,
max_tool_payload_bytes: 256 * 1024,
max_tool_output_bytes: 256 * 1024,
max_concurrent_tools_per_transaction: 16,
max_queued_tools_per_transaction: 64,
max_continuations: 32,
max_provider_exchanges: 64,
max_continuation_context_bytes: 2 * 1024 * 1024,
max_total_provider_input_bytes: 16 * 1024 * 1024,
max_total_provider_output_bytes: 16 * 1024 * 1024,
max_diagnostic_count: 64,
max_diagnostic_bytes: 1024,
transaction_deadline: Duration::from_secs(600),
cleanup_deadline: Duration::from_secs(30),
terminal_event_delivery_deadline: Duration::from_secs(10),
callback_deadline: Duration::from_secs(5),
}
}
}
#[derive(Clone, Debug, Error, PartialEq, Eq)]
pub enum LimitsError {
#[error("limit must be non-zero: {0}")]
ZeroCapacity(&'static str),
#[error("inconsistent limits: {0}")]
Inconsistent(&'static str),
}
mod duration_secs {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::time::Duration;
pub fn serialize<S>(d: &Duration, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
d.as_secs().serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: Deserializer<'de>,
{
let secs = u64::deserialize(deserializer)?;
Ok(Duration::from_secs(secs))
}
}
impl TransactionLimits {
pub fn validate(&self) -> Result<(), LimitsError> {
for (name, v) in [
("max_active_transactions", self.max_active_transactions),
("max_active_per_channel", self.max_active_per_channel),
("max_actor_commands", self.max_actor_commands),
("max_actor_command_bytes", self.max_actor_command_bytes),
("max_event_queue", self.max_event_queue),
("max_event_queue_bytes", self.max_event_queue_bytes),
("max_input_bytes", self.max_input_bytes),
("max_messages", self.max_messages),
("max_content_parts", self.max_content_parts),
("max_tools_per_transaction", self.max_tools_per_transaction),
("max_tool_schema_bytes", self.max_tool_schema_bytes),
("max_tool_payload_bytes", self.max_tool_payload_bytes),
("max_tool_output_bytes", self.max_tool_output_bytes),
(
"max_concurrent_tools_per_transaction",
self.max_concurrent_tools_per_transaction,
),
(
"max_queued_tools_per_transaction",
self.max_queued_tools_per_transaction,
),
("max_continuations", self.max_continuations.max(1)), ("max_provider_exchanges", self.max_provider_exchanges),
(
"max_continuation_context_bytes",
self.max_continuation_context_bytes,
),
(
"max_total_provider_input_bytes",
self.max_total_provider_input_bytes,
),
(
"max_total_provider_output_bytes",
self.max_total_provider_output_bytes,
),
("max_diagnostic_count", self.max_diagnostic_count),
("max_diagnostic_bytes", self.max_diagnostic_bytes),
] {
if name == "max_continuations" {
continue;
}
if v == 0 {
return Err(LimitsError::ZeroCapacity(name));
}
}
if self.callback_deadline.is_zero() {
return Err(LimitsError::ZeroCapacity("callback_deadline"));
}
if self.cleanup_deadline.is_zero() {
return Err(LimitsError::ZeroCapacity("cleanup_deadline"));
}
if self.terminal_event_delivery_deadline.is_zero() {
return Err(LimitsError::ZeroCapacity(
"terminal_event_delivery_deadline",
));
}
if self.transaction_deadline.is_zero() {
return Err(LimitsError::ZeroCapacity("transaction_deadline"));
}
if self.max_active_per_channel > self.max_active_transactions {
return Err(LimitsError::Inconsistent(
"max_active_per_channel exceeds max_active_transactions",
));
}
if self.max_event_queue_bytes < self.max_event_queue {
return Err(LimitsError::Inconsistent(
"max_event_queue_bytes smaller than max_event_queue items",
));
}
Ok(())
}
}