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 seed_supported: Option<bool>,
127 #[serde(default)]
128 pub top_k_supported: Option<bool>,
129 #[serde(default)]
130 pub temperature_supported: Option<bool>,
131 #[serde(default)]
132 pub top_p_supported: Option<bool>,
133 #[serde(default)]
134 pub frequency_penalty_supported: Option<bool>,
135 #[serde(default)]
136 pub presence_penalty_supported: Option<bool>,
137}
138
139pub(super) fn overlay_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
141 if src.is_some() {
142 dst.clone_from(src);
143 }
144}
145
146pub(super) fn fill_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
148 if dst.is_none() {
149 dst.clone_from(src);
150 }
151}
152
153macro_rules! merge_provider_defaults {
157 ($dst:expr, $src:expr, $op:path) => {{
158 $op(&mut $dst.message_wire_format, &$src.message_wire_format);
159 $op(
160 &mut $dst.native_tool_wire_format,
161 &$src.native_tool_wire_format,
162 );
163 $op(
164 &mut $dst.image_url_input_supported,
165 &$src.image_url_input_supported,
166 );
167 $op(
168 &mut $dst.file_upload_wire_format,
169 &$src.file_upload_wire_format,
170 );
171 $op(&mut $dst.reasoning_wire_format, &$src.reasoning_wire_format);
172 $op(&mut $dst.files_api_supported, &$src.files_api_supported);
173 $op(&mut $dst.batch_api, &$src.batch_api);
174 $op(&mut $dst.batch_wire_format, &$src.batch_wire_format);
175 $op(&mut $dst.batch_input_mode, &$src.batch_input_mode);
176 $op(
177 &mut $dst.batch_discount_percent,
178 &$src.batch_discount_percent,
179 );
180 $op(
181 &mut $dst.batch_turnaround_hours,
182 &$src.batch_turnaround_hours,
183 );
184 $op(&mut $dst.seed_supported, &$src.seed_supported);
185 $op(&mut $dst.top_k_supported, &$src.top_k_supported);
186 $op(&mut $dst.temperature_supported, &$src.temperature_supported);
187 $op(&mut $dst.top_p_supported, &$src.top_p_supported);
188 $op(
189 &mut $dst.frequency_penalty_supported,
190 &$src.frequency_penalty_supported,
191 );
192 $op(
193 &mut $dst.presence_penalty_supported,
194 &$src.presence_penalty_supported,
195 );
196 }};
197}
198
199impl ProviderDefaults {
200 pub(super) fn overlay(&mut self, other: &ProviderDefaults) {
201 merge_provider_defaults!(self, other, overlay_opt);
202 }
203
204 pub(super) fn fill_missing_from(&mut self, other: &ProviderDefaults) {
205 merge_provider_defaults!(self, other, fill_opt);
206 }
207
208 pub(super) fn has_any_field(&self) -> bool {
209 self.message_wire_format.is_some()
210 || self.native_tool_wire_format.is_some()
211 || self.image_url_input_supported.is_some()
212 || self.file_upload_wire_format.is_some()
213 || self.reasoning_wire_format.is_some()
214 || self.files_api_supported.is_some()
215 || self.batch_api.is_some()
216 || self.batch_wire_format.is_some()
217 || self.batch_input_mode.is_some()
218 || self.batch_discount_percent.is_some()
219 || self.batch_turnaround_hours.is_some()
220 || self.seed_supported.is_some()
221 || self.top_k_supported.is_some()
222 || self.temperature_supported.is_some()
223 || self.top_p_supported.is_some()
224 || self.frequency_penalty_supported.is_some()
225 || self.presence_penalty_supported.is_some()
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub enum WireDialect {
240 Anthropic,
244 OpenAiCompat,
247 Ollama,
249 Gemini,
251}
252
253impl WireDialect {
254 pub fn from_message_wire_format(value: &str) -> WireDialect {
260 match value {
261 "anthropic" => WireDialect::Anthropic,
262 "ollama" => WireDialect::Ollama,
263 "gemini" => WireDialect::Gemini,
264 _ => WireDialect::OpenAiCompat,
265 }
266 }
267
268 pub fn as_str(self) -> &'static str {
270 match self {
271 WireDialect::Anthropic => "anthropic",
272 WireDialect::OpenAiCompat => "openai",
273 WireDialect::Ollama => "ollama",
274 WireDialect::Gemini => "gemini",
275 }
276 }
277
278 pub fn is_anthropic(self) -> bool {
280 matches!(self, WireDialect::Anthropic)
281 }
282
283 pub fn is_ollama(self) -> bool {
285 matches!(self, WireDialect::Ollama)
286 }
287
288 pub fn is_gemini(self) -> bool {
290 matches!(self, WireDialect::Gemini)
291 }
292}
293
294#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
299#[serde(rename_all = "snake_case")]
300pub enum ComputerUseStyle {
301 NativeAnthropic,
303 NativeOpenai,
305 Grounded,
307 Function,
309}
310
311#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
315#[serde(rename_all = "snake_case")]
316pub enum ScreenshotScaling {
317 Xga,
319 Original,
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
327pub struct Capabilities {
328 pub native_tools: bool,
329 pub message_wire_format: WireDialect,
330 pub native_tool_wire_format: String,
331 pub defer_loading: bool,
332 pub tool_search: Vec<String>,
333 pub responses_api: bool,
334 pub hosted_tools: Vec<String>,
335 pub remote_mcp: bool,
336 pub conversation_state: bool,
337 pub compaction: bool,
338 pub background_mode: bool,
339 pub batch_api: bool,
340 pub batch_wire_format: Option<String>,
341 pub batch_input_mode: Option<String>,
342 pub batch_discount_percent: Option<u32>,
343 pub batch_turnaround_hours: Option<u32>,
344 pub tool_approval_policy: Option<String>,
345 pub max_tools: Option<u32>,
346 pub prompt_caching: bool,
347 pub cache_breakpoint_style: String,
348 pub vision: bool,
349 pub audio: bool,
350 pub pdf: bool,
351 pub video: bool,
352 pub files_api_supported: bool,
353 pub file_upload_wire_format: Option<String>,
354 pub structured_output: Option<String>,
355 pub json_schema: Option<String>,
357 pub prefers_xml_scaffolding: bool,
358 pub reserved_tool_call_token: bool,
360 pub prefers_markdown_scaffolding: bool,
361 pub structured_output_mode: String,
362 pub supports_assistant_prefill: bool,
363 pub prefers_role_developer: bool,
364 pub prefers_xml_tools: bool,
365 pub thinking_block_style: String,
366 pub emits_inline_reasoning: bool,
376 pub thinking_modes: Vec<String>,
377 pub interleaved_thinking_supported: bool,
378 pub anthropic_beta_features: Vec<String>,
379 pub vision_supported: bool,
380 pub image_url_input_supported: bool,
381 pub preserve_thinking: bool,
382 pub server_parser: String,
383 pub honors_chat_template_kwargs: bool,
384 pub chat_template_options_field: Option<String>,
385 pub requires_completion_tokens: bool,
386 pub chat_completions_unsupported: bool,
390 pub requires_streaming: bool,
391 pub reasoning_effort_supported: bool,
392 pub reasoning_effort_levels: Vec<String>,
393 pub reasoning_none_supported: bool,
394 pub max_thinking_budget: Option<i64>,
397 pub reasoning_disable_supported: bool,
398 pub reasoning_required_for_tools: bool,
400 pub reasoning_text_promotable: bool,
401 pub reasoning_wire_format: Option<String>,
402 pub seed_supported: bool,
403 pub top_k_supported: bool,
404 pub temperature_supported: bool,
405 pub top_p_supported: bool,
406 pub frequency_penalty_supported: bool,
407 pub presence_penalty_supported: bool,
408 pub allowed_tool_choice_modes: Vec<String>,
409 pub requires_tool_result_adjacency: bool,
410 pub supports_parallel_tool_calls: bool,
411 pub tools_exclude_response_format: bool,
412 pub recommended_endpoint: Option<String>,
413 pub text_tool_wire_format_supported: bool,
414 pub preferred_tool_format: Option<String>,
415 pub tool_mode_parity: Option<String>,
416 pub tool_mode_parity_notes: Option<String>,
417 pub thinking_disable_directive: Option<String>,
418 pub auto_reasoning_overrides: BTreeMap<String, String>,
421 pub provider_route_denylist: Vec<String>,
425 pub openrouter_provider_order: Vec<String>,
429 pub serving_precision: String,
432 pub computer_use_style: Option<ComputerUseStyle>,
436 pub screenshot_scaling: Option<ScreenshotScaling>,
439 pub safety_ack_flow: bool,
443}
444
445impl Default for Capabilities {
446 fn default() -> Self {
447 Self {
448 native_tools: false,
449 message_wire_format: WireDialect::OpenAiCompat,
450 native_tool_wire_format: "openai".to_string(),
451 defer_loading: false,
452 tool_search: Vec::new(),
453 responses_api: false,
454 hosted_tools: Vec::new(),
455 remote_mcp: false,
456 conversation_state: false,
457 compaction: false,
458 background_mode: false,
459 batch_api: false,
460 batch_wire_format: None,
461 batch_input_mode: None,
462 batch_discount_percent: None,
463 batch_turnaround_hours: None,
464 tool_approval_policy: None,
465 max_tools: None,
466 prompt_caching: false,
467 cache_breakpoint_style: "none".to_string(),
468 vision: false,
469 audio: false,
470 pdf: false,
471 video: false,
472 files_api_supported: false,
473 file_upload_wire_format: None,
474 structured_output: None,
475 json_schema: None,
476 prefers_xml_scaffolding: false,
477 reserved_tool_call_token: false,
478 prefers_markdown_scaffolding: false,
479 structured_output_mode: "none".to_string(),
480 supports_assistant_prefill: false,
481 prefers_role_developer: false,
482 prefers_xml_tools: false,
483 thinking_block_style: "none".to_string(),
484 emits_inline_reasoning: false,
485 thinking_modes: Vec::new(),
486 interleaved_thinking_supported: false,
487 anthropic_beta_features: Vec::new(),
488 vision_supported: false,
489 image_url_input_supported: true,
490 preserve_thinking: false,
491 server_parser: "none".to_string(),
492 honors_chat_template_kwargs: false,
493 chat_template_options_field: None,
494 requires_completion_tokens: false,
495 chat_completions_unsupported: false,
496 requires_streaming: false,
497 reasoning_effort_supported: false,
498 reasoning_effort_levels: Vec::new(),
499 reasoning_none_supported: false,
500 max_thinking_budget: None,
501 reasoning_disable_supported: true,
502 reasoning_required_for_tools: false,
503 reasoning_text_promotable: true,
504 reasoning_wire_format: None,
505 seed_supported: true,
506 top_k_supported: true,
507 temperature_supported: true,
508 top_p_supported: true,
509 frequency_penalty_supported: true,
510 presence_penalty_supported: true,
511 allowed_tool_choice_modes: Vec::new(),
512 requires_tool_result_adjacency: false,
513 supports_parallel_tool_calls: true,
514 tools_exclude_response_format: false,
515 recommended_endpoint: None,
516 text_tool_wire_format_supported: true,
517 preferred_tool_format: None,
518 tool_mode_parity: None,
519 tool_mode_parity_notes: None,
520 thinking_disable_directive: None,
521 auto_reasoning_overrides: BTreeMap::new(),
522 provider_route_denylist: Vec::new(),
523 openrouter_provider_order: Vec::new(),
524 serving_precision: "unverified".to_string(),
525 computer_use_style: None,
526 screenshot_scaling: None,
527 safety_ack_flow: false,
528 }
529 }
530}