use std::{
collections::{BTreeMap, HashMap},
sync::LazyLock,
};
use enumset::enum_set;
use crate::{
anthropic_wire::{mantle_anthropic_adaptive_reasoning, mantle_anthropic_haiku_reasoning},
capabilities::{
MediaKind, MediaSupport, ReasoningCapability, ReasoningMode, ReasoningParamConflicts,
},
config::ReasoningEffort,
media::SourceKind,
};
#[derive(Debug, Clone)]
pub(super) struct MantleModelCapabilities {
pub(super) context_window: u32,
pub(super) supports_streaming: bool,
pub(super) supports_json_schema: bool,
pub(super) reasoning: Option<ReasoningCapability>,
pub(super) media_support: BTreeMap<MediaKind, MediaSupport>,
}
const fn mantle_image_support() -> MediaSupport {
MediaSupport {
sources: enum_set!(SourceKind::Url | SourceKind::InlineBytes),
formats: &["png", "jpeg", "webp", "gif"],
max_bytes: Some(20 * 1024 * 1024),
max_count_per_message: None,
}
}
const fn mantle_voxtral_audio_support() -> MediaSupport {
MediaSupport {
sources: enum_set!(SourceKind::InlineBytes),
formats: &["wav", "mpeg", "mp3", "flac"],
max_bytes: Some(25 * 1024 * 1024),
max_count_per_message: None,
}
}
const fn openai_reasoning_conflicts() -> ReasoningParamConflicts {
ReasoningParamConflicts {
temperature_forbidden: true,
top_k_forbidden: false,
top_p_allowed_range: None,
}
}
const fn mantle_gpt5_reasoning() -> ReasoningCapability {
ReasoningCapability {
supported_modes: enum_set!(ReasoningMode::Adaptive),
supported_efforts: enum_set!(
ReasoningEffort::None
| ReasoningEffort::Low
| ReasoningEffort::Medium
| ReasoningEffort::High
| ReasoningEffort::XHigh
),
manual_budget_range: None,
conflicts: openai_reasoning_conflicts(),
sampling_params_removed: false,
}
}
const fn mantle_gpt_oss_reasoning() -> ReasoningCapability {
ReasoningCapability {
supported_modes: enum_set!(ReasoningMode::Adaptive),
supported_efforts: enum_set!(
ReasoningEffort::Low | ReasoningEffort::Medium | ReasoningEffort::High
),
manual_budget_range: None,
conflicts: openai_reasoning_conflicts(),
sampling_params_removed: false,
}
}
const fn mantle_open_thinking_reasoning() -> ReasoningCapability {
ReasoningCapability {
supported_modes: enum_set!(ReasoningMode::Adaptive),
supported_efforts: enum_set!(
ReasoningEffort::Low | ReasoningEffort::Medium | ReasoningEffort::High
),
manual_budget_range: None,
conflicts: openai_reasoning_conflicts(),
sampling_params_removed: false,
}
}
pub(super) static MODEL_CAPABILITIES: LazyLock<HashMap<&'static str, MantleModelCapabilities>> =
LazyLock::new(|| {
let mut m = HashMap::new();
for id in ["openai.gpt-5.5", "openai.gpt-5.4"] {
m.insert(
id,
MantleModelCapabilities {
context_window: 1_000_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_gpt5_reasoning()),
media_support: BTreeMap::new(),
},
);
}
for id in ["openai.gpt-5.5-2026-04-23", "openai.gpt-5.4-2026-03-05"] {
m.insert(
id,
MantleModelCapabilities {
context_window: 1_000_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_gpt5_reasoning()),
media_support: BTreeMap::new(),
},
);
}
for id in ["openai.gpt-oss-120b", "openai.gpt-oss-20b"] {
m.insert(
id,
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_gpt_oss_reasoning()),
media_support: BTreeMap::new(),
},
);
}
for id in [
"openai.gpt-oss-safeguard-120b",
"openai.gpt-oss-safeguard-20b",
] {
m.insert(
id,
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: BTreeMap::new(),
},
);
}
m.insert(
"anthropic.claude-haiku-4-5",
MantleModelCapabilities {
context_window: 200_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_anthropic_haiku_reasoning()),
media_support: BTreeMap::new(),
},
);
m.insert(
"anthropic.claude-opus-4-7",
MantleModelCapabilities {
context_window: 1_000_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_anthropic_adaptive_reasoning()),
media_support: BTreeMap::new(),
},
);
m.insert(
"anthropic.claude-opus-4-8",
MantleModelCapabilities {
context_window: 1_000_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_anthropic_adaptive_reasoning()),
media_support: BTreeMap::new(),
},
);
m.insert(
"anthropic.claude-mythos-preview",
MantleModelCapabilities {
context_window: 1_000_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_anthropic_adaptive_reasoning()),
media_support: BTreeMap::new(),
},
);
m.insert(
"deepseek.v3.1",
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: BTreeMap::new(),
},
);
m.insert(
"deepseek.v3.2",
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: BTreeMap::new(),
},
);
m.insert(
"mistral.devstral-2-123b",
MantleModelCapabilities {
context_window: 256_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: BTreeMap::new(),
},
);
m.insert(
"mistral.magistral-small-2509",
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: BTreeMap::new(),
},
);
m.insert(
"mistral.mistral-large-3-675b-instruct",
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: BTreeMap::new(),
},
);
for id in [
"mistral.ministral-3-3b-instruct",
"mistral.ministral-3-8b-instruct",
"mistral.ministral-3-14b-instruct",
] {
m.insert(
id,
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: BTreeMap::new(),
},
);
}
for id in [
"mistral.voxtral-mini-3b-2507",
"mistral.voxtral-small-24b-2507",
] {
let mut media = BTreeMap::new();
media.insert(MediaKind::Audio, mantle_voxtral_audio_support());
m.insert(
id,
MantleModelCapabilities {
context_window: 32_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: media,
},
);
}
for id in [
"google.gemma-3-4b-it",
"google.gemma-3-12b-it",
"google.gemma-3-27b-it",
] {
let mut media = BTreeMap::new();
media.insert(MediaKind::Image, mantle_image_support());
m.insert(
id,
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: media,
},
);
}
for id in [
"qwen.qwen3-32b",
"qwen.qwen3-235b-a22b-2507",
"qwen.qwen3-next-80b-a3b-instruct",
"qwen.qwen3-coder-30b-a3b-instruct",
"qwen.qwen3-coder-480b-a35b-instruct",
"qwen.qwen3-coder-next",
] {
m.insert(
id,
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: BTreeMap::new(),
},
);
}
{
let mut media = BTreeMap::new();
media.insert(MediaKind::Image, mantle_image_support());
m.insert(
"qwen.qwen3-vl-235b-a22b-instruct",
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: media,
},
);
}
for id in [
"nvidia.nemotron-nano-9b-v2",
"nvidia.nemotron-nano-3-30b",
"nvidia.nemotron-super-3-120b",
] {
m.insert(
id,
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: BTreeMap::new(),
},
);
}
{
let mut media = BTreeMap::new();
media.insert(MediaKind::Image, mantle_image_support());
m.insert(
"nvidia.nemotron-nano-12b-v2",
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: media,
},
);
}
for id in [
"minimax.minimax-m2",
"minimax.minimax-m2.1",
"minimax.minimax-m2.5",
] {
m.insert(
id,
MantleModelCapabilities {
context_window: 200_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: BTreeMap::new(),
},
);
}
m.insert(
"moonshotai.kimi-k2-thinking",
MantleModelCapabilities {
context_window: 200_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: BTreeMap::new(),
},
);
m.insert(
"moonshotai.kimi-k2.5",
MantleModelCapabilities {
context_window: 256_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: Some(mantle_open_thinking_reasoning()),
media_support: BTreeMap::new(),
},
);
for id in [
"zai.glm-4.6",
"zai.glm-4.7",
"zai.glm-4.7-flash",
"zai.glm-5",
] {
m.insert(
id,
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: BTreeMap::new(),
},
);
}
{
let mut media = BTreeMap::new();
media.insert(MediaKind::Image, mantle_image_support());
m.insert(
"writer.palmyra-vision-7b",
MantleModelCapabilities {
context_window: 128_000,
supports_streaming: true,
supports_json_schema: true,
reasoning: None,
media_support: media,
},
);
}
m
});