Wire K3 thinking-effort levels end to end (fixes 'model does not support reasoning effort')

The live /models wire (verified against api.kimi.com) marks every Kimi
Code model supports_thinking_type: "only" and gives K3 a think_efforts
block {support, valid_efforts: [low, high, max], default_effort: max} —
both of which the F4 sync discarded, hardcoding
supports_reasoning_effort: false. Every effort selection was therefore
rejected with 'current model does not support reasoning effort'.

- kigi-models: WireModel gains supports_thinking_type + WireThinkEfforts;
  "only" forces the always_thinking capability.
- models_fetch: think_efforts maps into the catalog entry — wire tokens
  stay the option ids/labels (max/Max), canonical values map via the
  ReasoningEffort parser (max → Xhigh), default_effort marks the default.
  This lights up the existing /model <model> [effort] two-phase completion
  and the /effort menu with the server's own vocabulary.
- kimi_compat: the effort level rides the wire as thinking.effort
  ({"type": "enabled", "effort": "low"} is accepted live; invalid
  levels are a 400). Only the canonical-vs-wire spelling divergence
  (xhigh → max) is renamed; levels pass through verbatim so a contract
  violation surfaces instead of being clamped away.

Live acceptance: kigi -m kimi-code/k3 --reasoning-effort max -p ... round
trips against api.kimi.com, and the refreshed models_cache.json carries
low/high/max with the max default.
This commit is contained in:
2026-07-17 18:22:00 -04:00
parent 0e3d43128e
commit 5f3f9509b9
4 changed files with 263 additions and 15 deletions
@@ -382,9 +382,12 @@ async fn request_carries_bearer_kigi_ua_and_kimi_dialect_body() {
assert_eq!(body["stream"], json!(true));
assert_eq!(body["stream_options"], json!({ "include_usage": true }));
// -- Thinking mapping (kimi.py:214-223): effort → thinking, no
// reasoning_effort on the wire.
assert_eq!(body["thinking"], json!({ "type": "enabled" }));
// -- Thinking mapping (kimi.py:214-223 + live think_efforts wire):
// effort → thinking {type, effort}, no reasoning_effort on the wire.
assert_eq!(
body["thinking"],
json!({ "type": "enabled", "effort": "high" })
);
assert_eq!(body.get("reasoning_effort"), None);
// -- Message adaptations.