[Rust Frontend] Fix Kimi K3 reasoning_effort="none" handling (#53043)

Co-authored-by: Bugen Zhao <i@bugenzhao.com> Signed-off-by: reidliu41 <reid201711@gmail.com>

Reid committed Aug 21, 2026 at 10:47 UTC 2adf4b9e2a915bd30876ab0c5dba2243f54fe5b7
2 files changed +50 -9
rust/src/chat/src/renderer/kimi_k3/encoding.rs
+19 -6
@@ -236,11 +236,15 @@ fn thinking_enabled(request: &ChatRequest) -> Result<bool> {
236 if let Some(enable_thinking) = request.parse_template_bool("enable_thinking")? {
237 return Ok(enable_thinking);
238 }
239 + if let Some(reasoning_effort) = request.chat_options.reasoning_effort {
240 + return Ok(reasoning_effort != crate::request::ReasoningEffort::None);
241 + }
242 Ok(request
243 .chat_options
241 - .reasoning_effort
242 - .map(|effort| effort != crate::request::ReasoningEffort::None)
243 - .unwrap_or(true))
244 + .template_kwargs
245 + .get("reasoning_effort")
246 + .and_then(Value::as_str)
247 + != Some("none"))
248 }
249
250 fn thinking_effort(request: &ChatRequest) -> Result<String> {
@@ -251,13 +255,22 @@ fn thinking_effort(request: &ChatRequest) -> Result<String> {
255 ))
256 })?
257 } else if let Some(effort) = request.chat_options.reasoning_effort {
254 - effort.as_str()
258 + if effort == crate::request::ReasoningEffort::None {
259 + DEFAULT_THINKING_EFFORT
260 + } else {
261 + effort.as_str()
262 + }
263 } else if let Some(value) = request.chat_options.template_kwargs.get("reasoning_effort") {
256 - value.as_str().ok_or_else(|| {
264 + let effort = value.as_str().ok_or_else(|| {
265 Error::ChatTemplate(format!(
266 "template kwarg `reasoning_effort` must be a string, got {value}"
267 ))
260 - })?
268 + })?;
269 + if effort == "none" {
270 + DEFAULT_THINKING_EFFORT
271 + } else {
272 + effort
273 + }
274 } else {
275 DEFAULT_THINKING_EFFORT
276 };
rust/src/chat/src/renderer/kimi_k3/tests.rs
+31 -3
@@ -329,16 +329,44 @@ fn native_k3_kwargs_take_precedence() {
329
330 #[test]
331 fn standard_none_disables_thinking() {
332 + let mut request = crate::request::ChatRequest::for_test();
333 + request
334 + .chat_options
335 + .template_kwargs
336 + .insert("reasoning_effort".to_string(), json!("none"));
337 +
338 + let rendered = render_request(&request);
339 +
340 + assert!(!rendered.contains("type=\"thinking-effort\""));
341 + assert!(rendered.ends_with("<|open|>response<|sep|>"));
342 +}
343 +
344 +#[test]
345 +fn native_thinking_true_overrides_standard_none() {
346 let mut request = crate::request::ChatRequest::for_test();
347 request.chat_options.template_kwargs.extend([
334 - ("enable_thinking".to_string(), json!(false)),
348 + ("thinking".to_string(), json!(true)),
349 ("reasoning_effort".to_string(), json!("none")),
350 ]);
351
352 let rendered = render_request(&request);
353
340 - assert!(!rendered.contains("type=\"thinking-effort\""));
341 - assert!(rendered.ends_with("<|open|>response<|sep|>"));
354 + assert!(rendered.contains("thinking_effort=max"));
355 + assert!(rendered.ends_with("<|open|>think<|sep|>"));
356 +}
357 +
358 +#[test]
359 +fn enable_thinking_true_overrides_standard_none() {
360 + let mut request = crate::request::ChatRequest::for_test();
361 + request.chat_options.template_kwargs.extend([
362 + ("enable_thinking".to_string(), json!(true)),
363 + ("reasoning_effort".to_string(), json!("none")),
364 + ]);
365 +
366 + let rendered = render_request(&request);
367 +
368 + assert!(rendered.contains("thinking_effort=max"));
369 + assert!(rendered.ends_with("<|open|>think<|sep|>"));
370 }
371
372 #[test]