use serde::{Deserialize, Serialize};
use super::candidate::PricingSku;
use super::capabilities::RouteCapabilities;
use super::ids::{ModelId, ProviderId, WireModelId};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct RouteLimits {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_tokens: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output_tokens: Option<u64>,
}
impl RouteLimits {
#[must_use]
pub const fn has_known_limit(self) -> bool {
self.context_tokens.is_some() || self.input_tokens.is_some() || self.output_tokens.is_some()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ProviderModelOffering {
pub provider: ProviderId,
pub canonical_model: Option<ModelId>,
pub wire_model_id: WireModelId,
pub endpoint_key: String,
pub default_for_provider: bool,
pub limits: RouteLimits,
pub capabilities: RouteCapabilities,
pub pricing: PricingSku,
}
pub(crate) const OPENCODE_ZEN_RESPONSES_MODELS: &[&str] = &[
"gpt-5.6-sol",
"gpt-5.6-terra",
"gpt-5.6-luna",
"gpt-5.5",
"gpt-5.5-pro",
"gpt-5.4",
"gpt-5.4-pro",
"gpt-5.4-mini",
"gpt-5.4-nano",
"gpt-5.3-codex",
"gpt-5.3-codex-spark",
"gpt-5.2",
"gpt-5.2-codex",
"gpt-5.1",
"gpt-5.1-codex",
"gpt-5.1-codex-max",
"gpt-5.1-codex-mini",
"gpt-5",
"gpt-5-codex",
"gpt-5-nano",
];
pub(crate) const OPENCODE_ZEN_MESSAGES_MODELS: &[&str] = &[
"claude-fable-5",
"claude-opus-4-8",
"claude-opus-4-7",
"claude-opus-4-6",
"claude-opus-4-5",
"claude-sonnet-5",
"claude-sonnet-4-6",
"claude-sonnet-4-5",
"claude-haiku-4-5",
"qwen3.7-max",
"qwen3.7-plus",
"qwen3.6-plus",
"qwen3.5-plus",
];
pub(crate) const OPENCODE_ZEN_CHAT_MODELS: &[&str] = &[
"deepseek-v4-pro",
"deepseek-v4-flash",
"minimax-m3",
"minimax-m2.7",
"minimax-m2.5",
"glm-5.2",
"glm-5.1",
"glm-5",
"kimi-k2.5",
"kimi-k2.6",
"kimi-k2.7-code",
"grok-4.5",
"grok-build-0.1",
"big-pickle",
"mimo-v2.5-free",
"north-mini-code-free",
"nemotron-3-ultra-free",
"deepseek-v4-flash-free",
];
#[must_use]
pub fn bundled_offerings() -> Vec<ProviderModelOffering> {
let provider = ProviderId::from("opencode-zen");
let groups = [
("responses", OPENCODE_ZEN_RESPONSES_MODELS),
("messages", OPENCODE_ZEN_MESSAGES_MODELS),
("chat", OPENCODE_ZEN_CHAT_MODELS),
];
groups
.into_iter()
.flat_map(|(endpoint_key, models)| {
let provider = provider.clone();
models.iter().map(move |model| ProviderModelOffering {
provider: provider.clone(),
canonical_model: None,
wire_model_id: WireModelId::from(*model),
endpoint_key: endpoint_key.to_string(),
default_for_provider: *model == "gpt-5.5",
limits: RouteLimits::default(),
capabilities: RouteCapabilities::default(),
pricing: PricingSku::UnknownOrStale,
})
})
.collect()
}