use std::collections::BTreeMap;
use serde::Deserialize;
use super::rule::ProviderRule;
#[derive(Debug, Clone, Deserialize, Default)]
pub struct CapabilitiesFile {
#[serde(default)]
pub provider: BTreeMap<String, Vec<ProviderRule>>,
#[serde(default)]
pub provider_defaults: BTreeMap<String, ProviderDefaults>,
#[serde(default)]
pub provider_family: BTreeMap<String, String>,
}
#[derive(Debug, Clone, Deserialize, Default)]
pub struct ProviderDefaults {
#[serde(default)]
pub message_wire_format: Option<String>,
#[serde(default)]
pub native_tool_wire_format: Option<String>,
#[serde(default)]
pub image_url_input_supported: Option<bool>,
#[serde(default)]
pub file_upload_wire_format: Option<String>,
#[serde(default)]
pub reasoning_wire_format: Option<String>,
#[serde(default)]
pub files_api_supported: Option<bool>,
#[serde(default)]
pub seed_supported: Option<bool>,
#[serde(default)]
pub top_k_supported: Option<bool>,
#[serde(default)]
pub temperature_supported: Option<bool>,
#[serde(default)]
pub top_p_supported: Option<bool>,
#[serde(default)]
pub frequency_penalty_supported: Option<bool>,
#[serde(default)]
pub presence_penalty_supported: Option<bool>,
}
pub(super) fn overlay_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
if src.is_some() {
dst.clone_from(src);
}
}
pub(super) fn fill_opt<T: Clone>(dst: &mut Option<T>, src: &Option<T>) {
if dst.is_none() {
dst.clone_from(src);
}
}
macro_rules! merge_provider_defaults {
($dst:expr, $src:expr, $op:path) => {{
$op(&mut $dst.message_wire_format, &$src.message_wire_format);
$op(
&mut $dst.native_tool_wire_format,
&$src.native_tool_wire_format,
);
$op(
&mut $dst.image_url_input_supported,
&$src.image_url_input_supported,
);
$op(
&mut $dst.file_upload_wire_format,
&$src.file_upload_wire_format,
);
$op(&mut $dst.reasoning_wire_format, &$src.reasoning_wire_format);
$op(&mut $dst.files_api_supported, &$src.files_api_supported);
$op(&mut $dst.seed_supported, &$src.seed_supported);
$op(&mut $dst.top_k_supported, &$src.top_k_supported);
$op(&mut $dst.temperature_supported, &$src.temperature_supported);
$op(&mut $dst.top_p_supported, &$src.top_p_supported);
$op(
&mut $dst.frequency_penalty_supported,
&$src.frequency_penalty_supported,
);
$op(
&mut $dst.presence_penalty_supported,
&$src.presence_penalty_supported,
);
}};
}
impl ProviderDefaults {
pub(super) fn overlay(&mut self, other: &ProviderDefaults) {
merge_provider_defaults!(self, other, overlay_opt);
}
pub(super) fn fill_missing_from(&mut self, other: &ProviderDefaults) {
merge_provider_defaults!(self, other, fill_opt);
}
pub(super) fn has_any_field(&self) -> bool {
self.message_wire_format.is_some()
|| self.native_tool_wire_format.is_some()
|| self.image_url_input_supported.is_some()
|| self.file_upload_wire_format.is_some()
|| self.reasoning_wire_format.is_some()
|| self.files_api_supported.is_some()
|| self.seed_supported.is_some()
|| self.top_k_supported.is_some()
|| self.temperature_supported.is_some()
|| self.top_p_supported.is_some()
|| self.frequency_penalty_supported.is_some()
|| self.presence_penalty_supported.is_some()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WireDialect {
Anthropic,
OpenAiCompat,
Ollama,
Gemini,
}
impl WireDialect {
pub fn from_message_wire_format(value: &str) -> WireDialect {
match value {
"anthropic" => WireDialect::Anthropic,
"ollama" => WireDialect::Ollama,
"gemini" => WireDialect::Gemini,
_ => WireDialect::OpenAiCompat,
}
}
pub fn as_str(self) -> &'static str {
match self {
WireDialect::Anthropic => "anthropic",
WireDialect::OpenAiCompat => "openai",
WireDialect::Ollama => "ollama",
WireDialect::Gemini => "gemini",
}
}
pub fn is_anthropic(self) -> bool {
matches!(self, WireDialect::Anthropic)
}
pub fn is_ollama(self) -> bool {
matches!(self, WireDialect::Ollama)
}
pub fn is_gemini(self) -> bool {
matches!(self, WireDialect::Gemini)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Capabilities {
pub native_tools: bool,
pub message_wire_format: WireDialect,
pub native_tool_wire_format: String,
pub defer_loading: bool,
pub tool_search: Vec<String>,
pub responses_api: bool,
pub hosted_tools: Vec<String>,
pub remote_mcp: bool,
pub conversation_state: bool,
pub compaction: bool,
pub background_mode: bool,
pub tool_approval_policy: Option<String>,
pub max_tools: Option<u32>,
pub prompt_caching: bool,
pub cache_breakpoint_style: String,
pub vision: bool,
pub audio: bool,
pub pdf: bool,
pub video: bool,
pub files_api_supported: bool,
pub file_upload_wire_format: Option<String>,
pub structured_output: Option<String>,
pub json_schema: Option<String>,
pub prefers_xml_scaffolding: bool,
pub reserved_tool_call_token: bool,
pub prefers_markdown_scaffolding: bool,
pub structured_output_mode: String,
pub supports_assistant_prefill: bool,
pub prefers_role_developer: bool,
pub prefers_xml_tools: bool,
pub thinking_block_style: String,
pub thinking_modes: Vec<String>,
pub interleaved_thinking_supported: bool,
pub anthropic_beta_features: Vec<String>,
pub vision_supported: bool,
pub image_url_input_supported: bool,
pub preserve_thinking: bool,
pub server_parser: String,
pub honors_chat_template_kwargs: bool,
pub chat_template_options_field: Option<String>,
pub requires_completion_tokens: bool,
pub chat_completions_unsupported: bool,
pub requires_streaming: bool,
pub reasoning_effort_supported: bool,
pub reasoning_effort_levels: Vec<String>,
pub reasoning_none_supported: bool,
pub max_thinking_budget: Option<i64>,
pub reasoning_disable_supported: bool,
pub reasoning_required_for_tools: bool,
pub reasoning_text_promotable: bool,
pub reasoning_wire_format: Option<String>,
pub seed_supported: bool,
pub top_k_supported: bool,
pub temperature_supported: bool,
pub top_p_supported: bool,
pub frequency_penalty_supported: bool,
pub presence_penalty_supported: bool,
pub allowed_tool_choice_modes: Vec<String>,
pub requires_tool_result_adjacency: bool,
pub supports_parallel_tool_calls: bool,
pub tools_exclude_response_format: bool,
pub recommended_endpoint: Option<String>,
pub text_tool_wire_format_supported: bool,
pub preferred_tool_format: Option<String>,
pub tool_mode_parity: Option<String>,
pub tool_mode_parity_notes: Option<String>,
pub thinking_disable_directive: Option<String>,
pub auto_reasoning_overrides: BTreeMap<String, String>,
pub provider_route_denylist: Vec<String>,
pub openrouter_provider_order: Vec<String>,
pub serving_precision: String,
}
impl Default for Capabilities {
fn default() -> Self {
Self {
native_tools: false,
message_wire_format: WireDialect::OpenAiCompat,
native_tool_wire_format: "openai".to_string(),
defer_loading: false,
tool_search: Vec::new(),
responses_api: false,
hosted_tools: Vec::new(),
remote_mcp: false,
conversation_state: false,
compaction: false,
background_mode: false,
tool_approval_policy: None,
max_tools: None,
prompt_caching: false,
cache_breakpoint_style: "none".to_string(),
vision: false,
audio: false,
pdf: false,
video: false,
files_api_supported: false,
file_upload_wire_format: None,
structured_output: None,
json_schema: None,
prefers_xml_scaffolding: false,
reserved_tool_call_token: false,
prefers_markdown_scaffolding: false,
structured_output_mode: "none".to_string(),
supports_assistant_prefill: false,
prefers_role_developer: false,
prefers_xml_tools: false,
thinking_block_style: "none".to_string(),
thinking_modes: Vec::new(),
interleaved_thinking_supported: false,
anthropic_beta_features: Vec::new(),
vision_supported: false,
image_url_input_supported: true,
preserve_thinking: false,
server_parser: "none".to_string(),
honors_chat_template_kwargs: false,
chat_template_options_field: None,
requires_completion_tokens: false,
chat_completions_unsupported: false,
requires_streaming: false,
reasoning_effort_supported: false,
reasoning_effort_levels: Vec::new(),
reasoning_none_supported: false,
max_thinking_budget: None,
reasoning_disable_supported: true,
reasoning_required_for_tools: false,
reasoning_text_promotable: true,
reasoning_wire_format: None,
seed_supported: true,
top_k_supported: true,
temperature_supported: true,
top_p_supported: true,
frequency_penalty_supported: true,
presence_penalty_supported: true,
allowed_tool_choice_modes: Vec::new(),
requires_tool_result_adjacency: false,
supports_parallel_tool_calls: true,
tools_exclude_response_format: false,
recommended_endpoint: None,
text_tool_wire_format_supported: true,
preferred_tool_format: None,
tool_mode_parity: None,
tool_mode_parity_notes: None,
thinking_disable_directive: None,
auto_reasoning_overrides: BTreeMap::new(),
provider_route_denylist: Vec::new(),
openrouter_provider_order: Vec::new(),
serving_precision: "unverified".to_string(),
}
}
}