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 #[serde(default)]
159 pub stop_supported: Option<bool>,
160}
161
162pub(super) fn overlay_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
164 if src.is_some() {
165 dst.clone_from(src);
166 }
167}
168
169pub(super) fn fill_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
171 if dst.is_none() {
172 dst.clone_from(src);
173 }
174}
175
176macro_rules! merge_provider_defaults {
180 ($dst:expr, $src:expr, $op:path) => {{
181 $op(&mut $dst.message_wire_format, &$src.message_wire_format);
182 $op(
183 &mut $dst.native_tool_wire_format,
184 &$src.native_tool_wire_format,
185 );
186 $op(
187 &mut $dst.image_url_input_supported,
188 &$src.image_url_input_supported,
189 );
190 $op(
191 &mut $dst.file_upload_wire_format,
192 &$src.file_upload_wire_format,
193 );
194 $op(&mut $dst.reasoning_wire_format, &$src.reasoning_wire_format);
195 $op(&mut $dst.files_api_supported, &$src.files_api_supported);
196 $op(&mut $dst.batch_api, &$src.batch_api);
197 $op(&mut $dst.batch_wire_format, &$src.batch_wire_format);
198 $op(&mut $dst.batch_input_mode, &$src.batch_input_mode);
199 $op(
200 &mut $dst.batch_discount_percent,
201 &$src.batch_discount_percent,
202 );
203 $op(
204 &mut $dst.batch_turnaround_hours,
205 &$src.batch_turnaround_hours,
206 );
207 $op(&mut $dst.batch_max_requests, &$src.batch_max_requests);
208 $op(&mut $dst.batch_max_input_bytes, &$src.batch_max_input_bytes);
209 $op(
210 &mut $dst.batch_result_retention_days,
211 &$src.batch_result_retention_days,
212 );
213 $op(&mut $dst.batch_result_ordering, &$src.batch_result_ordering);
214 $op(&mut $dst.batch_partial_failure, &$src.batch_partial_failure);
215 $op(&mut $dst.batch_cancellation, &$src.batch_cancellation);
216 $op(&mut $dst.batch_security_notes, &$src.batch_security_notes);
217 $op(
218 &mut $dst.batch_operational_notes,
219 &$src.batch_operational_notes,
220 );
221 $op(&mut $dst.prompt_cache_ttls, &$src.prompt_cache_ttls);
222 $op(&mut $dst.seed_supported, &$src.seed_supported);
223 $op(&mut $dst.top_k_supported, &$src.top_k_supported);
224 $op(&mut $dst.temperature_supported, &$src.temperature_supported);
225 $op(&mut $dst.top_p_supported, &$src.top_p_supported);
226 $op(
227 &mut $dst.frequency_penalty_supported,
228 &$src.frequency_penalty_supported,
229 );
230 $op(
231 &mut $dst.presence_penalty_supported,
232 &$src.presence_penalty_supported,
233 );
234 $op(&mut $dst.stop_supported, &$src.stop_supported);
235 }};
236}
237
238impl ProviderDefaults {
239 pub(super) fn overlay(&mut self, other: &ProviderDefaults) {
240 merge_provider_defaults!(self, other, overlay_opt);
241 }
242
243 pub(super) fn fill_missing_from(&mut self, other: &ProviderDefaults) {
244 merge_provider_defaults!(self, other, fill_opt);
245 }
246
247 pub(super) fn has_any_field(&self) -> bool {
248 self.message_wire_format.is_some()
249 || self.native_tool_wire_format.is_some()
250 || self.image_url_input_supported.is_some()
251 || self.file_upload_wire_format.is_some()
252 || self.reasoning_wire_format.is_some()
253 || self.files_api_supported.is_some()
254 || self.batch_api.is_some()
255 || self.batch_wire_format.is_some()
256 || self.batch_input_mode.is_some()
257 || self.batch_discount_percent.is_some()
258 || self.batch_turnaround_hours.is_some()
259 || self.batch_max_requests.is_some()
260 || self.batch_max_input_bytes.is_some()
261 || self.batch_result_retention_days.is_some()
262 || self.batch_result_ordering.is_some()
263 || self.batch_partial_failure.is_some()
264 || self.batch_cancellation.is_some()
265 || self.batch_security_notes.is_some()
266 || self.batch_operational_notes.is_some()
267 || self.prompt_cache_ttls.is_some()
268 || self.seed_supported.is_some()
269 || self.top_k_supported.is_some()
270 || self.temperature_supported.is_some()
271 || self.top_p_supported.is_some()
272 || self.frequency_penalty_supported.is_some()
273 || self.presence_penalty_supported.is_some()
274 || self.stop_supported.is_some()
275 }
276}
277
278#[derive(Debug, Clone, Copy, PartialEq, Eq)]
288pub enum WireDialect {
289 Anthropic,
293 OpenAiCompat,
296 Ollama,
298 Gemini,
300}
301
302impl WireDialect {
303 pub fn from_message_wire_format(value: &str) -> WireDialect {
309 match value {
310 "anthropic" => WireDialect::Anthropic,
311 "ollama" => WireDialect::Ollama,
312 "gemini" => WireDialect::Gemini,
313 _ => WireDialect::OpenAiCompat,
314 }
315 }
316
317 pub fn as_str(self) -> &'static str {
319 match self {
320 WireDialect::Anthropic => "anthropic",
321 WireDialect::OpenAiCompat => "openai",
322 WireDialect::Ollama => "ollama",
323 WireDialect::Gemini => "gemini",
324 }
325 }
326
327 pub fn is_anthropic(self) -> bool {
329 matches!(self, WireDialect::Anthropic)
330 }
331
332 pub fn is_ollama(self) -> bool {
334 matches!(self, WireDialect::Ollama)
335 }
336
337 pub fn is_gemini(self) -> bool {
339 matches!(self, WireDialect::Gemini)
340 }
341}
342
343#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
348#[serde(rename_all = "snake_case")]
349pub enum ComputerUseStyle {
350 NativeAnthropic,
352 NativeOpenai,
354 Grounded,
356 Function,
358}
359
360#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
364#[serde(rename_all = "snake_case")]
365pub enum ScreenshotScaling {
366 Xga,
368 Original,
370}
371
372#[derive(Debug, Clone, PartialEq, Eq)]
376pub struct Capabilities {
377 pub native_tools: bool,
378 pub message_wire_format: WireDialect,
379 pub native_tool_wire_format: String,
380 pub defer_loading: bool,
381 pub tool_search: Vec<String>,
382 pub responses_api: bool,
383 pub hosted_tools: Vec<String>,
384 pub remote_mcp: bool,
385 pub conversation_state: bool,
386 pub compaction: bool,
387 pub background_mode: bool,
388 pub batch_api: bool,
389 pub batch_wire_format: Option<String>,
390 pub batch_input_mode: Option<String>,
391 pub batch_discount_percent: Option<u32>,
392 pub batch_turnaround_hours: Option<u32>,
393 pub batch_max_requests: Option<u64>,
394 pub batch_max_input_bytes: Option<u64>,
395 pub batch_result_retention_days: Option<u32>,
396 pub batch_result_ordering: Option<String>,
397 pub batch_partial_failure: Option<String>,
398 pub batch_cancellation: Option<String>,
399 pub batch_security_notes: Vec<String>,
400 pub batch_operational_notes: Vec<String>,
401 pub tool_approval_policy: Option<String>,
402 pub max_tools: Option<u32>,
403 pub prompt_caching: bool,
404 pub prompt_cache_ttls: Vec<String>,
405 pub cache_breakpoint_style: String,
406 pub vision: bool,
407 pub audio: bool,
408 pub pdf: bool,
409 pub video: bool,
410 pub files_api_supported: bool,
411 pub file_upload_wire_format: Option<String>,
412 pub structured_output: Option<String>,
413 pub json_schema: Option<String>,
415 pub prefers_xml_scaffolding: bool,
416 pub reserved_tool_call_token: bool,
418 pub prefers_markdown_scaffolding: bool,
419 pub structured_output_mode: String,
420 pub supports_assistant_prefill: bool,
421 pub prefers_role_developer: bool,
422 pub prefers_xml_tools: bool,
423 pub thinking_block_style: String,
424 pub emits_inline_reasoning: bool,
434 pub thinking_modes: Vec<String>,
435 pub interleaved_thinking_supported: bool,
436 pub anthropic_beta_features: Vec<String>,
437 pub vision_supported: bool,
438 pub image_url_input_supported: bool,
439 pub preserve_thinking: bool,
440 pub server_parser: String,
441 pub honors_chat_template_kwargs: bool,
442 pub chat_template_options_field: Option<String>,
443 pub requires_completion_tokens: bool,
444 pub chat_completions_unsupported: bool,
448 pub reasoning_tools_require_responses: bool,
450 pub requires_streaming: bool,
451 pub reasoning_effort_supported: bool,
452 pub reasoning_effort_levels: Vec<String>,
453 pub reasoning_none_supported: bool,
454 pub max_thinking_budget: Option<i64>,
457 pub reasoning_disable_supported: bool,
458 pub reasoning_required_for_tools: bool,
460 pub reasoning_text_promotable: bool,
461 pub reasoning_wire_format: Option<String>,
462 pub seed_supported: bool,
463 pub top_k_supported: bool,
464 pub temperature_supported: bool,
465 pub top_p_supported: bool,
466 pub frequency_penalty_supported: bool,
467 pub presence_penalty_supported: bool,
468 pub stop_supported: bool,
469 pub allowed_tool_choice_modes: Vec<String>,
470 pub requires_tool_result_adjacency: bool,
471 pub supports_parallel_tool_calls: bool,
472 pub tools_exclude_response_format: bool,
473 pub recommended_endpoint: Option<String>,
474 pub text_tool_wire_format_supported: bool,
475 pub preferred_tool_format: Option<String>,
476 pub tool_mode_parity: Option<String>,
477 pub tool_mode_parity_notes: Option<String>,
478 pub thinking_disable_directive: Option<String>,
479 pub auto_reasoning_overrides: BTreeMap<String, String>,
482 pub provider_route_denylist: Vec<String>,
486 pub openrouter_provider_order: Vec<String>,
490 pub serving_precision: String,
493 pub computer_use_style: Option<ComputerUseStyle>,
497 pub screenshot_scaling: Option<ScreenshotScaling>,
500 pub safety_ack_flow: bool,
504}
505
506impl Default for Capabilities {
507 fn default() -> Self {
508 Self {
509 native_tools: false,
510 message_wire_format: WireDialect::OpenAiCompat,
511 native_tool_wire_format: "openai".to_string(),
512 defer_loading: false,
513 tool_search: Vec::new(),
514 responses_api: false,
515 hosted_tools: Vec::new(),
516 remote_mcp: false,
517 conversation_state: false,
518 compaction: false,
519 background_mode: false,
520 batch_api: false,
521 batch_wire_format: None,
522 batch_input_mode: None,
523 batch_discount_percent: None,
524 batch_turnaround_hours: None,
525 batch_max_requests: None,
526 batch_max_input_bytes: None,
527 batch_result_retention_days: None,
528 batch_result_ordering: None,
529 batch_partial_failure: None,
530 batch_cancellation: None,
531 batch_security_notes: Vec::new(),
532 batch_operational_notes: Vec::new(),
533 tool_approval_policy: None,
534 max_tools: None,
535 prompt_caching: false,
536 prompt_cache_ttls: Vec::new(),
537 cache_breakpoint_style: "none".to_string(),
538 vision: false,
539 audio: false,
540 pdf: false,
541 video: false,
542 files_api_supported: false,
543 file_upload_wire_format: None,
544 structured_output: None,
545 json_schema: None,
546 prefers_xml_scaffolding: false,
547 reserved_tool_call_token: false,
548 prefers_markdown_scaffolding: false,
549 structured_output_mode: "none".to_string(),
550 supports_assistant_prefill: false,
551 prefers_role_developer: false,
552 prefers_xml_tools: false,
553 thinking_block_style: "none".to_string(),
554 emits_inline_reasoning: false,
555 thinking_modes: Vec::new(),
556 interleaved_thinking_supported: false,
557 anthropic_beta_features: Vec::new(),
558 vision_supported: false,
559 image_url_input_supported: true,
560 preserve_thinking: false,
561 server_parser: "none".to_string(),
562 honors_chat_template_kwargs: false,
563 chat_template_options_field: None,
564 requires_completion_tokens: false,
565 chat_completions_unsupported: false,
566 reasoning_tools_require_responses: false,
567 requires_streaming: false,
568 reasoning_effort_supported: false,
569 reasoning_effort_levels: Vec::new(),
570 reasoning_none_supported: false,
571 max_thinking_budget: None,
572 reasoning_disable_supported: true,
573 reasoning_required_for_tools: false,
574 reasoning_text_promotable: false,
575 reasoning_wire_format: None,
576 seed_supported: true,
577 top_k_supported: true,
578 temperature_supported: true,
579 top_p_supported: true,
580 frequency_penalty_supported: true,
581 presence_penalty_supported: true,
582 stop_supported: true,
583 allowed_tool_choice_modes: Vec::new(),
584 requires_tool_result_adjacency: false,
585 supports_parallel_tool_calls: true,
586 tools_exclude_response_format: false,
587 recommended_endpoint: None,
588 text_tool_wire_format_supported: true,
589 preferred_tool_format: None,
590 tool_mode_parity: None,
591 tool_mode_parity_notes: None,
592 thinking_disable_directive: None,
593 auto_reasoning_overrides: BTreeMap::new(),
594 provider_route_denylist: Vec::new(),
595 openrouter_provider_order: Vec::new(),
596 serving_precision: "unverified".to_string(),
597 computer_use_style: None,
598 screenshot_scaling: None,
599 safety_ack_flow: false,
600 }
601 }
602}