From 4a1ecaf72453001e5fce35658cc14e6f91bb6f69 Mon Sep 17 00:00:00 2001 From: Azeem1985 Date: Tue, 25 Aug 2026 08:32:56 +0000 Subject: [PATCH] feat(llm): send an explicit reasoning_effort for Kimi models that support it (GRAPHIFY_KIMI_EFFORT) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kimi backend sent no reasoning_effort at all, so the server default ("high" for K3) applied silently — while the gemini block directly above already carried an effort setting. K3 advertises valid_efforts ["low","high","max"] on /models. The backend entry now carries `reasoning_effort`, default "max", overridable with GRAPHIFY_KIMI_EFFORT. It is forwarded by the existing request plumbing, so models that ignore the field are unaffected. Tests: effort assembles from GRAPHIFY_KIMI_EFFORT and defaults to max. --- CHANGELOG.md | 4 ++++ graphify/llm.py | 6 ++++++ tests/test_kimi_reasoning_effort.py | 31 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 tests/test_kimi_reasoning_effort.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 250990011..3d33beae3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Full release notes with details on each version: [GitHub Releases](https://github.com/safishamsi/graphify/releases) +## Unreleased + +- Feature: the `kimi` backend now sends an explicit `reasoning_effort` (default `max`, overridable via `GRAPHIFY_KIMI_EFFORT`) for Kimi models that support it — K3 advertises `valid_efforts ["low","high","max"]` on `/models`, and sending nothing let the server default (`"high"` for K3) apply silently while the gemini backend already carried an effort setting. Forwarded by the existing request plumbing; no behaviour change for models that ignore the field. + ## 0.9.49 (2026-08-24) - Feature: `graphify merge-graphs` now links a type declaration that two repos share — same fully-qualified namespace and name, from different repos — with a `same_type_as` edge, so a shared contract type is navigable across the repo boundary; two unrelated types that merely share a short name are not linked (#3007, thanks @durmazoguzhan). diff --git a/graphify/llm.py b/graphify/llm.py index ae2119773..158cb03ec 100644 --- a/graphify/llm.py +++ b/graphify/llm.py @@ -123,6 +123,12 @@ def _resolve_ollama_base_url(default: str) -> str: "pricing": {"input": 0.74, "output": 4.66}, # USD per 1M tokens "temperature": None, # kimi-k2.6 enforces its own fixed temperature; sending any value raises 400 "max_tokens": 16384, + # Reasoning effort for Kimi models that support it (K3 advertises + # valid_efforts ["low","high","max"] on /models). Sending nothing lets the + # server default ("high" for K3) apply silently, while the gemini block + # above carries an effort setting. Forwarded as `reasoning_effort` by the + # existing request plumbing; models that ignore the field are unaffected. + "reasoning_effort": os.environ.get("GRAPHIFY_KIMI_EFFORT", "max"), }, "ollama": { "base_url": _resolve_ollama_base_url("http://localhost:11434/v1"), diff --git a/tests/test_kimi_reasoning_effort.py b/tests/test_kimi_reasoning_effort.py new file mode 100644 index 000000000..f4cab00f3 --- /dev/null +++ b/tests/test_kimi_reasoning_effort.py @@ -0,0 +1,31 @@ +"""Tests for the kimi backend's reasoning_effort config (GRAPHIFY_KIMI_EFFORT). + +Kimi K3 advertises valid_efforts ["low","high","max"] on /models; sending +nothing let the server default ("high") apply silently. The backend config now +carries an explicit effort, defaulting to "max", overridable via env — the +same import-time pattern as ANTHROPIC_BASE_URL on the claude backend. +""" + +import importlib + +from graphify import llm + + +def test_kimi_reasoning_effort_defaults_to_max(monkeypatch): + monkeypatch.delenv("GRAPHIFY_KIMI_EFFORT", raising=False) + reloaded = importlib.reload(llm) + try: + assert reloaded.BACKENDS["kimi"]["reasoning_effort"] == "max" + finally: + monkeypatch.undo() + importlib.reload(llm) + + +def test_kimi_reasoning_effort_env_override(monkeypatch): + monkeypatch.setenv("GRAPHIFY_KIMI_EFFORT", "low") + reloaded = importlib.reload(llm) + try: + assert reloaded.BACKENDS["kimi"]["reasoning_effort"] == "low" + finally: + monkeypatch.undo() + importlib.reload(llm)