1use std::collections::BTreeMap;
10
11use serde::Deserialize;
12
13use super::rule::ProviderRule;
14
15#[derive(Debug, Clone, Deserialize, Default)]
18pub struct CapabilitiesFile {
19 #[serde(default)]
24 pub provider: BTreeMap<String, Vec<ProviderRule>>,
25 #[serde(default)]
30 pub provider_defaults: BTreeMap<String, ProviderDefaults>,
31 #[serde(default)]
34 pub provider_family: BTreeMap<String, String>,
35 #[serde(default)]
40 pub provider_limits: BTreeMap<String, ProviderLimits>,
41}
42
43#[derive(Debug, Clone, Deserialize, Default, PartialEq)]
47pub struct ProviderLimits {
48 #[serde(default)]
51 pub max_concurrency: Option<u32>,
52 #[serde(default)]
55 pub min_concurrency: Option<u32>,
56 #[serde(default)]
58 pub rpm: Option<u32>,
59 #[serde(default)]
62 pub tpm: Option<u64>,
63 #[serde(default)]
66 pub adaptive: Option<bool>,
67 #[serde(default)]
69 pub backoff: Option<GovernorBackoff>,
70}
71
72#[derive(Debug, Clone, Deserialize, PartialEq)]
76pub struct GovernorBackoff {
77 #[serde(default)]
79 pub base_ms: Option<u64>,
80 #[serde(default)]
82 pub max_ms: Option<u64>,
83 #[serde(default)]
85 pub multiplier: Option<f64>,
86 #[serde(default)]
88 pub jitter: Option<bool>,
89}
90
91#[derive(Debug, Clone, Deserialize, Default)]
93pub struct ProviderDefaults {
94 #[serde(default)]
97 pub message_wire_format: Option<String>,
98 #[serde(default)]
101 pub native_tool_wire_format: Option<String>,
102 #[serde(default)]
104 pub image_url_input_supported: Option<bool>,
105 #[serde(default)]
108 pub file_upload_wire_format: Option<String>,
109 #[serde(default)]
112 pub reasoning_wire_format: Option<String>,
113 #[serde(default)]
114 pub files_api_supported: Option<bool>,
115 #[serde(default)]
116 pub batch_api: Option<bool>,
117 #[serde(default)]
118 pub batch_wire_format: Option<String>,
119 #[serde(default)]
120 pub batch_input_mode: Option<String>,
121 #[serde(default)]
122 pub batch_discount_percent: Option<u32>,
123 #[serde(default)]
124 pub batch_turnaround_hours: Option<u32>,
125 #[serde(default)]
126 pub batch_max_requests: Option<u64>,
127 #[serde(default)]
128 pub batch_max_input_bytes: Option<u64>,
129 #[serde(default)]
130 pub batch_result_retention_days: Option<u32>,
131 #[serde(default)]
132 pub batch_result_ordering: Option<String>,
133 #[serde(default)]
134 pub batch_partial_failure: Option<String>,
135 #[serde(default)]
136 pub batch_cancellation: Option<String>,
137 #[serde(default)]
138 pub batch_security_notes: Option<Vec<String>>,
139 #[serde(default)]
140 pub batch_operational_notes: Option<Vec<String>>,
141 #[serde(default)]
145 pub prompt_cache_ttls: Option<Vec<String>>,
146 #[serde(default)]
147 pub seed_supported: Option<bool>,
148 #[serde(default)]
149 pub top_k_supported: Option<bool>,
150 #[serde(default)]
151 pub temperature_supported: Option<bool>,
152 #[serde(default)]
153 pub top_p_supported: Option<bool>,
154 #[serde(default)]
155 pub frequency_penalty_supported: Option<bool>,
156 #[serde(default)]
157 pub presence_penalty_supported: Option<bool>,
158}
159
160pub(super) fn overlay_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
162 if src.is_some() {
163 dst.clone_from(src);
164 }
165}
166
167pub(super) fn fill_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
169 if dst.is_none() {
170 dst.clone_from(src);
171 }
172}
173
174macro_rules! merge_provider_defaults {
178 ($dst:expr, $src:expr, $op:path) => {{
179 $op(&mut $dst.message_wire_format, &$src.message_wire_format);
180 $op(
181 &mut $dst.native_tool_wire_format,
182 &$src.native_tool_wire_format,
183 );
184 $op(
185 &mut $dst.image_url_input_supported,
186 &$src.image_url_input_supported,
187 );
188 $op(
189 &mut $dst.file_upload_wire_format,
190 &$src.file_upload_wire_format,
191 );
192 $op(&mut $dst.reasoning_wire_format, &$src.reasoning_wire_format);
193 $op(&mut $dst.files_api_supported, &$src.files_api_supported);
194 $op(&mut $dst.batch_api, &$src.batch_api);
195 $op(&mut $dst.batch_wire_format, &$src.batch_wire_format);
196 $op(&mut $dst.batch_input_mode, &$src.batch_input_mode);
197 $op(
198 &mut $dst.batch_discount_percent,
199 &$src.batch_discount_percent,
200 );
201 $op(
202 &mut $dst.batch_turnaround_hours,
203 &$src.batch_turnaround_hours,
204 );
205 $op(&mut $dst.batch_max_requests, &$src.batch_max_requests);
206 $op(&mut $dst.batch_max_input_bytes, &$src.batch_max_input_bytes);
207 $op(
208 &mut $dst.batch_result_retention_days,
209 &$src.batch_result_retention_days,
210 );
211 $op(&mut $dst.batch_result_ordering, &$src.batch_result_ordering);
212 $op(&mut $dst.batch_partial_failure, &$src.batch_partial_failure);
213 $op(&mut $dst.batch_cancellation, &$src.batch_cancellation);
214 $op(&mut $dst.batch_security_notes, &$src.batch_security_notes);
215 $op(
216 &mut $dst.batch_operational_notes,
217 &$src.batch_operational_notes,
218 );
219 $op(&mut $dst.prompt_cache_ttls, &$src.prompt_cache_ttls);
220 $op(&mut $dst.seed_supported, &$src.seed_supported);
221 $op(&mut $dst.top_k_supported, &$src.top_k_supported);
222 $op(&mut $dst.temperature_supported, &$src.temperature_supported);
223 $op(&mut $dst.top_p_supported, &$src.top_p_supported);
224 $op(
225 &mut $dst.frequency_penalty_supported,
226 &$src.frequency_penalty_supported,
227 );
228 $op(
229 &mut $dst.presence_penalty_supported,
230 &$src.presence_penalty_supported,
231 );
232 }};
233}
234
235impl ProviderDefaults {
236 pub(super) fn overlay(&mut self, other: &ProviderDefaults) {
237 merge_provider_defaults!(self, other, overlay_opt);
238 }
239
240 pub(super) fn fill_missing_from(&mut self, other: &ProviderDefaults) {
241 merge_provider_defaults!(self, other, fill_opt);
242 }
243
244 pub(super) fn has_any_field(&self) -> bool {
245 self.message_wire_format.is_some()
246 || self.native_tool_wire_format.is_some()
247 || self.image_url_input_supported.is_some()
248 || self.file_upload_wire_format.is_some()
249 || self.reasoning_wire_format.is_some()
250 || self.files_api_supported.is_some()
251 || self.batch_api.is_some()
252 || self.batch_wire_format.is_some()
253 || self.batch_input_mode.is_some()
254 || self.batch_discount_percent.is_some()
255 || self.batch_turnaround_hours.is_some()
256 || self.batch_max_requests.is_some()
257 || self.batch_max_input_bytes.is_some()
258 || self.batch_result_retention_days.is_some()
259 || self.batch_result_ordering.is_some()
260 || self.batch_partial_failure.is_some()
261 || self.batch_cancellation.is_some()
262 || self.batch_security_notes.is_some()
263 || self.batch_operational_notes.is_some()
264 || self.prompt_cache_ttls.is_some()
265 || self.seed_supported.is_some()
266 || self.top_k_supported.is_some()
267 || self.temperature_supported.is_some()
268 || self.top_p_supported.is_some()
269 || self.frequency_penalty_supported.is_some()
270 || self.presence_penalty_supported.is_some()
271 }
272}
273
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
284pub enum WireDialect {
285 Anthropic,
289 OpenAiCompat,
292 Ollama,
294 Gemini,
296}
297
298impl WireDialect {
299 pub fn from_message_wire_format(value: &str) -> WireDialect {
305 match value {
306 "anthropic" => WireDialect::Anthropic,
307 "ollama" => WireDialect::Ollama,
308 "gemini" => WireDialect::Gemini,
309 _ => WireDialect::OpenAiCompat,
310 }
311 }
312
313 pub fn as_str(self) -> &'static str {
315 match self {
316 WireDialect::Anthropic => "anthropic",
317 WireDialect::OpenAiCompat => "openai",
318 WireDialect::Ollama => "ollama",
319 WireDialect::Gemini => "gemini",
320 }
321 }
322
323 pub fn is_anthropic(self) -> bool {
325 matches!(self, WireDialect::Anthropic)
326 }
327
328 pub fn is_ollama(self) -> bool {
330 matches!(self, WireDialect::Ollama)
331 }
332
333 pub fn is_gemini(self) -> bool {
335 matches!(self, WireDialect::Gemini)
336 }
337}
338
339#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
344#[serde(rename_all = "snake_case")]
345pub enum ComputerUseStyle {
346 NativeAnthropic,
348 NativeOpenai,
350 Grounded,
352 Function,
354}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
360#[serde(rename_all = "snake_case")]
361pub enum ScreenshotScaling {
362 Xga,
364 Original,
366}
367
368#[derive(Debug, Clone, PartialEq, Eq)]
372pub struct Capabilities {
373 pub native_tools: bool,
374 pub message_wire_format: WireDialect,
375 pub native_tool_wire_format: String,
376 pub defer_loading: bool,
377 pub tool_search: Vec<String>,
378 pub responses_api: bool,
379 pub hosted_tools: Vec<String>,
380 pub remote_mcp: bool,
381 pub conversation_state: bool,
382 pub compaction: bool,
383 pub background_mode: bool,
384 pub batch_api: bool,
385 pub batch_wire_format: Option<String>,
386 pub batch_input_mode: Option<String>,
387 pub batch_discount_percent: Option<u32>,
388 pub batch_turnaround_hours: Option<u32>,
389 pub batch_max_requests: Option<u64>,
390 pub batch_max_input_bytes: Option<u64>,
391 pub batch_result_retention_days: Option<u32>,
392 pub batch_result_ordering: Option<String>,
393 pub batch_partial_failure: Option<String>,
394 pub batch_cancellation: Option<String>,
395 pub batch_security_notes: Vec<String>,
396 pub batch_operational_notes: Vec<String>,
397 pub tool_approval_policy: Option<String>,
398 pub max_tools: Option<u32>,
399 pub prompt_caching: bool,
400 pub prompt_cache_ttls: Vec<String>,
401 pub cache_breakpoint_style: String,
402 pub vision: bool,
403 pub audio: bool,
404 pub pdf: bool,
405 pub video: bool,
406 pub files_api_supported: bool,
407 pub file_upload_wire_format: Option<String>,
408 pub structured_output: Option<String>,
409 pub json_schema: Option<String>,
411 pub prefers_xml_scaffolding: bool,
412 pub reserved_tool_call_token: bool,
414 pub prefers_markdown_scaffolding: bool,
415 pub structured_output_mode: String,
416 pub supports_assistant_prefill: bool,
417 pub prefers_role_developer: bool,
418 pub prefers_xml_tools: bool,
419 pub thinking_block_style: String,
420 pub emits_inline_reasoning: bool,
430 pub thinking_modes: Vec<String>,
431 pub interleaved_thinking_supported: bool,
432 pub anthropic_beta_features: Vec<String>,
433 pub vision_supported: bool,
434 pub image_url_input_supported: bool,
435 pub preserve_thinking: bool,
436 pub server_parser: String,
437 pub honors_chat_template_kwargs: bool,
438 pub chat_template_options_field: Option<String>,
439 pub requires_completion_tokens: bool,
440 pub chat_completions_unsupported: bool,
444 pub requires_streaming: bool,
445 pub reasoning_effort_supported: bool,
446 pub reasoning_effort_levels: Vec<String>,
447 pub reasoning_none_supported: bool,
448 pub max_thinking_budget: Option<i64>,
451 pub reasoning_disable_supported: bool,
452 pub reasoning_required_for_tools: bool,
454 pub reasoning_text_promotable: bool,
455 pub reasoning_wire_format: Option<String>,
456 pub seed_supported: bool,
457 pub top_k_supported: bool,
458 pub temperature_supported: bool,
459 pub top_p_supported: bool,
460 pub frequency_penalty_supported: bool,
461 pub presence_penalty_supported: bool,
462 pub allowed_tool_choice_modes: Vec<String>,
463 pub requires_tool_result_adjacency: bool,
464 pub supports_parallel_tool_calls: bool,
465 pub tools_exclude_response_format: bool,
466 pub recommended_endpoint: Option<String>,
467 pub text_tool_wire_format_supported: bool,
468 pub preferred_tool_format: Option<String>,
469 pub tool_mode_parity: Option<String>,
470 pub tool_mode_parity_notes: Option<String>,
471 pub thinking_disable_directive: Option<String>,
472 pub auto_reasoning_overrides: BTreeMap<String, String>,
475 pub provider_route_denylist: Vec<String>,
479 pub openrouter_provider_order: Vec<String>,
483 pub serving_precision: String,
486 pub computer_use_style: Option<ComputerUseStyle>,
490 pub screenshot_scaling: Option<ScreenshotScaling>,
493 pub safety_ack_flow: bool,
497}
498
499impl Default for Capabilities {
500 fn default() -> Self {
501 Self {
502 native_tools: false,
503 message_wire_format: WireDialect::OpenAiCompat,
504 native_tool_wire_format: "openai".to_string(),
505 defer_loading: false,
506 tool_search: Vec::new(),
507 responses_api: false,
508 hosted_tools: Vec::new(),
509 remote_mcp: false,
510 conversation_state: false,
511 compaction: false,
512 background_mode: false,
513 batch_api: false,
514 batch_wire_format: None,
515 batch_input_mode: None,
516 batch_discount_percent: None,
517 batch_turnaround_hours: None,
518 batch_max_requests: None,
519 batch_max_input_bytes: None,
520 batch_result_retention_days: None,
521 batch_result_ordering: None,
522 batch_partial_failure: None,
523 batch_cancellation: None,
524 batch_security_notes: Vec::new(),
525 batch_operational_notes: Vec::new(),
526 tool_approval_policy: None,
527 max_tools: None,
528 prompt_caching: false,
529 prompt_cache_ttls: Vec::new(),
530 cache_breakpoint_style: "none".to_string(),
531 vision: false,
532 audio: false,
533 pdf: false,
534 video: false,
535 files_api_supported: false,
536 file_upload_wire_format: None,
537 structured_output: None,
538 json_schema: None,
539 prefers_xml_scaffolding: false,
540 reserved_tool_call_token: false,
541 prefers_markdown_scaffolding: false,
542 structured_output_mode: "none".to_string(),
543 supports_assistant_prefill: false,
544 prefers_role_developer: false,
545 prefers_xml_tools: false,
546 thinking_block_style: "none".to_string(),
547 emits_inline_reasoning: false,
548 thinking_modes: Vec::new(),
549 interleaved_thinking_supported: false,
550 anthropic_beta_features: Vec::new(),
551 vision_supported: false,
552 image_url_input_supported: true,
553 preserve_thinking: false,
554 server_parser: "none".to_string(),
555 honors_chat_template_kwargs: false,
556 chat_template_options_field: None,
557 requires_completion_tokens: false,
558 chat_completions_unsupported: false,
559 requires_streaming: false,
560 reasoning_effort_supported: false,
561 reasoning_effort_levels: Vec::new(),
562 reasoning_none_supported: false,
563 max_thinking_budget: None,
564 reasoning_disable_supported: true,
565 reasoning_required_for_tools: false,
566 reasoning_text_promotable: true,
567 reasoning_wire_format: None,
568 seed_supported: true,
569 top_k_supported: true,
570 temperature_supported: true,
571 top_p_supported: true,
572 frequency_penalty_supported: true,
573 presence_penalty_supported: true,
574 allowed_tool_choice_modes: Vec::new(),
575 requires_tool_result_adjacency: false,
576 supports_parallel_tool_calls: true,
577 tools_exclude_response_format: false,
578 recommended_endpoint: None,
579 text_tool_wire_format_supported: true,
580 preferred_tool_format: None,
581 tool_mode_parity: None,
582 tool_mode_parity_notes: None,
583 thinking_disable_directive: None,
584 auto_reasoning_overrides: BTreeMap::new(),
585 provider_route_denylist: Vec::new(),
586 openrouter_provider_order: Vec::new(),
587 serving_precision: "unverified".to_string(),
588 computer_use_style: None,
589 screenshot_scaling: None,
590 safety_ack_flow: false,
591 }
592 }
593}