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)]
142 pub seed_supported: Option<bool>,
143 #[serde(default)]
144 pub top_k_supported: Option<bool>,
145 #[serde(default)]
146 pub temperature_supported: Option<bool>,
147 #[serde(default)]
148 pub top_p_supported: Option<bool>,
149 #[serde(default)]
150 pub frequency_penalty_supported: Option<bool>,
151 #[serde(default)]
152 pub presence_penalty_supported: Option<bool>,
153}
154
155pub(super) fn overlay_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
157 if src.is_some() {
158 dst.clone_from(src);
159 }
160}
161
162pub(super) fn fill_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
164 if dst.is_none() {
165 dst.clone_from(src);
166 }
167}
168
169macro_rules! merge_provider_defaults {
173 ($dst:expr, $src:expr, $op:path) => {{
174 $op(&mut $dst.message_wire_format, &$src.message_wire_format);
175 $op(
176 &mut $dst.native_tool_wire_format,
177 &$src.native_tool_wire_format,
178 );
179 $op(
180 &mut $dst.image_url_input_supported,
181 &$src.image_url_input_supported,
182 );
183 $op(
184 &mut $dst.file_upload_wire_format,
185 &$src.file_upload_wire_format,
186 );
187 $op(&mut $dst.reasoning_wire_format, &$src.reasoning_wire_format);
188 $op(&mut $dst.files_api_supported, &$src.files_api_supported);
189 $op(&mut $dst.batch_api, &$src.batch_api);
190 $op(&mut $dst.batch_wire_format, &$src.batch_wire_format);
191 $op(&mut $dst.batch_input_mode, &$src.batch_input_mode);
192 $op(
193 &mut $dst.batch_discount_percent,
194 &$src.batch_discount_percent,
195 );
196 $op(
197 &mut $dst.batch_turnaround_hours,
198 &$src.batch_turnaround_hours,
199 );
200 $op(&mut $dst.batch_max_requests, &$src.batch_max_requests);
201 $op(&mut $dst.batch_max_input_bytes, &$src.batch_max_input_bytes);
202 $op(
203 &mut $dst.batch_result_retention_days,
204 &$src.batch_result_retention_days,
205 );
206 $op(&mut $dst.batch_result_ordering, &$src.batch_result_ordering);
207 $op(&mut $dst.batch_partial_failure, &$src.batch_partial_failure);
208 $op(&mut $dst.batch_cancellation, &$src.batch_cancellation);
209 $op(&mut $dst.batch_security_notes, &$src.batch_security_notes);
210 $op(
211 &mut $dst.batch_operational_notes,
212 &$src.batch_operational_notes,
213 );
214 $op(&mut $dst.seed_supported, &$src.seed_supported);
215 $op(&mut $dst.top_k_supported, &$src.top_k_supported);
216 $op(&mut $dst.temperature_supported, &$src.temperature_supported);
217 $op(&mut $dst.top_p_supported, &$src.top_p_supported);
218 $op(
219 &mut $dst.frequency_penalty_supported,
220 &$src.frequency_penalty_supported,
221 );
222 $op(
223 &mut $dst.presence_penalty_supported,
224 &$src.presence_penalty_supported,
225 );
226 }};
227}
228
229impl ProviderDefaults {
230 pub(super) fn overlay(&mut self, other: &ProviderDefaults) {
231 merge_provider_defaults!(self, other, overlay_opt);
232 }
233
234 pub(super) fn fill_missing_from(&mut self, other: &ProviderDefaults) {
235 merge_provider_defaults!(self, other, fill_opt);
236 }
237
238 pub(super) fn has_any_field(&self) -> bool {
239 self.message_wire_format.is_some()
240 || self.native_tool_wire_format.is_some()
241 || self.image_url_input_supported.is_some()
242 || self.file_upload_wire_format.is_some()
243 || self.reasoning_wire_format.is_some()
244 || self.files_api_supported.is_some()
245 || self.batch_api.is_some()
246 || self.batch_wire_format.is_some()
247 || self.batch_input_mode.is_some()
248 || self.batch_discount_percent.is_some()
249 || self.batch_turnaround_hours.is_some()
250 || self.batch_max_requests.is_some()
251 || self.batch_max_input_bytes.is_some()
252 || self.batch_result_retention_days.is_some()
253 || self.batch_result_ordering.is_some()
254 || self.batch_partial_failure.is_some()
255 || self.batch_cancellation.is_some()
256 || self.batch_security_notes.is_some()
257 || self.batch_operational_notes.is_some()
258 || self.seed_supported.is_some()
259 || self.top_k_supported.is_some()
260 || self.temperature_supported.is_some()
261 || self.top_p_supported.is_some()
262 || self.frequency_penalty_supported.is_some()
263 || self.presence_penalty_supported.is_some()
264 }
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq)]
277pub enum WireDialect {
278 Anthropic,
282 OpenAiCompat,
285 Ollama,
287 Gemini,
289}
290
291impl WireDialect {
292 pub fn from_message_wire_format(value: &str) -> WireDialect {
298 match value {
299 "anthropic" => WireDialect::Anthropic,
300 "ollama" => WireDialect::Ollama,
301 "gemini" => WireDialect::Gemini,
302 _ => WireDialect::OpenAiCompat,
303 }
304 }
305
306 pub fn as_str(self) -> &'static str {
308 match self {
309 WireDialect::Anthropic => "anthropic",
310 WireDialect::OpenAiCompat => "openai",
311 WireDialect::Ollama => "ollama",
312 WireDialect::Gemini => "gemini",
313 }
314 }
315
316 pub fn is_anthropic(self) -> bool {
318 matches!(self, WireDialect::Anthropic)
319 }
320
321 pub fn is_ollama(self) -> bool {
323 matches!(self, WireDialect::Ollama)
324 }
325
326 pub fn is_gemini(self) -> bool {
328 matches!(self, WireDialect::Gemini)
329 }
330}
331
332#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
337#[serde(rename_all = "snake_case")]
338pub enum ComputerUseStyle {
339 NativeAnthropic,
341 NativeOpenai,
343 Grounded,
345 Function,
347}
348
349#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
353#[serde(rename_all = "snake_case")]
354pub enum ScreenshotScaling {
355 Xga,
357 Original,
359}
360
361#[derive(Debug, Clone, PartialEq, Eq)]
365pub struct Capabilities {
366 pub native_tools: bool,
367 pub message_wire_format: WireDialect,
368 pub native_tool_wire_format: String,
369 pub defer_loading: bool,
370 pub tool_search: Vec<String>,
371 pub responses_api: bool,
372 pub hosted_tools: Vec<String>,
373 pub remote_mcp: bool,
374 pub conversation_state: bool,
375 pub compaction: bool,
376 pub background_mode: bool,
377 pub batch_api: bool,
378 pub batch_wire_format: Option<String>,
379 pub batch_input_mode: Option<String>,
380 pub batch_discount_percent: Option<u32>,
381 pub batch_turnaround_hours: Option<u32>,
382 pub batch_max_requests: Option<u64>,
383 pub batch_max_input_bytes: Option<u64>,
384 pub batch_result_retention_days: Option<u32>,
385 pub batch_result_ordering: Option<String>,
386 pub batch_partial_failure: Option<String>,
387 pub batch_cancellation: Option<String>,
388 pub batch_security_notes: Vec<String>,
389 pub batch_operational_notes: Vec<String>,
390 pub tool_approval_policy: Option<String>,
391 pub max_tools: Option<u32>,
392 pub prompt_caching: bool,
393 pub cache_breakpoint_style: String,
394 pub vision: bool,
395 pub audio: bool,
396 pub pdf: bool,
397 pub video: bool,
398 pub files_api_supported: bool,
399 pub file_upload_wire_format: Option<String>,
400 pub structured_output: Option<String>,
401 pub json_schema: Option<String>,
403 pub prefers_xml_scaffolding: bool,
404 pub reserved_tool_call_token: bool,
406 pub prefers_markdown_scaffolding: bool,
407 pub structured_output_mode: String,
408 pub supports_assistant_prefill: bool,
409 pub prefers_role_developer: bool,
410 pub prefers_xml_tools: bool,
411 pub thinking_block_style: String,
412 pub emits_inline_reasoning: bool,
422 pub thinking_modes: Vec<String>,
423 pub interleaved_thinking_supported: bool,
424 pub anthropic_beta_features: Vec<String>,
425 pub vision_supported: bool,
426 pub image_url_input_supported: bool,
427 pub preserve_thinking: bool,
428 pub server_parser: String,
429 pub honors_chat_template_kwargs: bool,
430 pub chat_template_options_field: Option<String>,
431 pub requires_completion_tokens: bool,
432 pub chat_completions_unsupported: bool,
436 pub requires_streaming: bool,
437 pub reasoning_effort_supported: bool,
438 pub reasoning_effort_levels: Vec<String>,
439 pub reasoning_none_supported: bool,
440 pub max_thinking_budget: Option<i64>,
443 pub reasoning_disable_supported: bool,
444 pub reasoning_required_for_tools: bool,
446 pub reasoning_text_promotable: bool,
447 pub reasoning_wire_format: Option<String>,
448 pub seed_supported: bool,
449 pub top_k_supported: bool,
450 pub temperature_supported: bool,
451 pub top_p_supported: bool,
452 pub frequency_penalty_supported: bool,
453 pub presence_penalty_supported: bool,
454 pub allowed_tool_choice_modes: Vec<String>,
455 pub requires_tool_result_adjacency: bool,
456 pub supports_parallel_tool_calls: bool,
457 pub tools_exclude_response_format: bool,
458 pub recommended_endpoint: Option<String>,
459 pub text_tool_wire_format_supported: bool,
460 pub preferred_tool_format: Option<String>,
461 pub tool_mode_parity: Option<String>,
462 pub tool_mode_parity_notes: Option<String>,
463 pub thinking_disable_directive: Option<String>,
464 pub auto_reasoning_overrides: BTreeMap<String, String>,
467 pub provider_route_denylist: Vec<String>,
471 pub openrouter_provider_order: Vec<String>,
475 pub serving_precision: String,
478 pub computer_use_style: Option<ComputerUseStyle>,
482 pub screenshot_scaling: Option<ScreenshotScaling>,
485 pub safety_ack_flow: bool,
489}
490
491impl Default for Capabilities {
492 fn default() -> Self {
493 Self {
494 native_tools: false,
495 message_wire_format: WireDialect::OpenAiCompat,
496 native_tool_wire_format: "openai".to_string(),
497 defer_loading: false,
498 tool_search: Vec::new(),
499 responses_api: false,
500 hosted_tools: Vec::new(),
501 remote_mcp: false,
502 conversation_state: false,
503 compaction: false,
504 background_mode: false,
505 batch_api: false,
506 batch_wire_format: None,
507 batch_input_mode: None,
508 batch_discount_percent: None,
509 batch_turnaround_hours: None,
510 batch_max_requests: None,
511 batch_max_input_bytes: None,
512 batch_result_retention_days: None,
513 batch_result_ordering: None,
514 batch_partial_failure: None,
515 batch_cancellation: None,
516 batch_security_notes: Vec::new(),
517 batch_operational_notes: Vec::new(),
518 tool_approval_policy: None,
519 max_tools: None,
520 prompt_caching: false,
521 cache_breakpoint_style: "none".to_string(),
522 vision: false,
523 audio: false,
524 pdf: false,
525 video: false,
526 files_api_supported: false,
527 file_upload_wire_format: None,
528 structured_output: None,
529 json_schema: None,
530 prefers_xml_scaffolding: false,
531 reserved_tool_call_token: false,
532 prefers_markdown_scaffolding: false,
533 structured_output_mode: "none".to_string(),
534 supports_assistant_prefill: false,
535 prefers_role_developer: false,
536 prefers_xml_tools: false,
537 thinking_block_style: "none".to_string(),
538 emits_inline_reasoning: false,
539 thinking_modes: Vec::new(),
540 interleaved_thinking_supported: false,
541 anthropic_beta_features: Vec::new(),
542 vision_supported: false,
543 image_url_input_supported: true,
544 preserve_thinking: false,
545 server_parser: "none".to_string(),
546 honors_chat_template_kwargs: false,
547 chat_template_options_field: None,
548 requires_completion_tokens: false,
549 chat_completions_unsupported: false,
550 requires_streaming: false,
551 reasoning_effort_supported: false,
552 reasoning_effort_levels: Vec::new(),
553 reasoning_none_supported: false,
554 max_thinking_budget: None,
555 reasoning_disable_supported: true,
556 reasoning_required_for_tools: false,
557 reasoning_text_promotable: true,
558 reasoning_wire_format: None,
559 seed_supported: true,
560 top_k_supported: true,
561 temperature_supported: true,
562 top_p_supported: true,
563 frequency_penalty_supported: true,
564 presence_penalty_supported: true,
565 allowed_tool_choice_modes: Vec::new(),
566 requires_tool_result_adjacency: false,
567 supports_parallel_tool_calls: true,
568 tools_exclude_response_format: false,
569 recommended_endpoint: None,
570 text_tool_wire_format_supported: true,
571 preferred_tool_format: None,
572 tool_mode_parity: None,
573 tool_mode_parity_notes: None,
574 thinking_disable_directive: None,
575 auto_reasoning_overrides: BTreeMap::new(),
576 provider_route_denylist: Vec::new(),
577 openrouter_provider_order: Vec::new(),
578 serving_precision: "unverified".to_string(),
579 computer_use_style: None,
580 screenshot_scaling: None,
581 safety_ack_flow: false,
582 }
583 }
584}