#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ReasoningPatch {
Effort(String),
Thinking(bool),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ReasoningError {
pub family: &'static str,
pub allowed: &'static str,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Family {
K3,
K27Code,
K26,
}
fn classify(model: &str) -> Option<Family> {
let m = model.to_ascii_lowercase();
if m.contains("k3") {
Some(Family::K3)
} else if m.contains("k2.7") || m.contains("kimi-for-coding") {
Some(Family::K27Code)
} else if m.contains("k2.6") {
Some(Family::K26)
} else {
None
}
}
fn is_on(s: &str) -> bool {
matches!(
s.trim().to_ascii_lowercase().as_str(),
"on" | "enabled" | "enable" | "true" | "max" | "high" | "low"
)
}
fn is_off(s: &str) -> bool {
matches!(
s.trim().to_ascii_lowercase().as_str(),
"off" | "disabled" | "disable" | "false" | "none"
)
}
pub fn resolve(model: &str, setting: &str) -> Result<Option<ReasoningPatch>, ReasoningError> {
let Some(family) = classify(model) else {
return Ok(None);
};
let s = setting.trim().to_ascii_lowercase();
match family {
Family::K3 => {
if s == "max" {
Ok(Some(ReasoningPatch::Effort("max".to_string())))
} else {
Err(ReasoningError {
family: "kimi-k3",
allowed: "max",
})
}
}
Family::K27Code => {
if is_on(&s) {
Ok(Some(ReasoningPatch::Thinking(true)))
} else {
Err(ReasoningError {
family: "kimi-k2.7-code",
allowed: "on (thinking is always enabled)",
})
}
}
Family::K26 => {
if is_on(&s) {
Ok(Some(ReasoningPatch::Thinking(true)))
} else if is_off(&s) {
Ok(Some(ReasoningPatch::Thinking(false)))
} else {
Err(ReasoningError {
family: "kimi-k2.6",
allowed: "on, off",
})
}
}
}
}
pub fn patch_fields(patch: &ReasoningPatch) -> (Option<String>, Option<serde_json::Value>) {
match patch {
ReasoningPatch::Effort(effort) => (Some(effort.clone()), None),
ReasoningPatch::Thinking(enabled) => {
let ty = if *enabled { "enabled" } else { "disabled" };
(None, Some(serde_json::json!({ "type": ty })))
}
}
}
pub fn resolve_fields(model: &str, setting: &str) -> (Option<String>, Option<serde_json::Value>) {
match resolve(model, setting) {
Ok(Some(patch)) => patch_fields(&patch),
_ => (None, None),
}
}