Temperature handling for Kimi and gpt-5.5 auxiliary/vision calls
Cluster 4661 · agent. Data as of
2 open items. Fix merged in #12773 on 20 Apr 2026. Check it covers your case.
The short answer
- Known?
- Yes. 21 related items grouped here, 5 issues already closed.
- Fix event
- A fix was merged to main in PR #12773. Check it covers your case. No release data is tracked.
- Tracker
- The maintainers' tracker marks this
partially resolved, deciding PRs #12773, #12861, #12862, #115850. That is separate from the fix event. - Still open
- 1 issue and 1 PR.
Next step: read #12773, then check whether your report is the one still open.
Read #12773Pick up a Lab taskMost members concern temperature values sent to Kimi/Moonshot models (kimi-for-coding, kimi-k2.5, kimi-k2.7-code) in auxiliary and vision calls, including endpoint-specific overrides and honoring config. A smaller part covers omitting temperature for the OpenAI gpt-5.5 family, so the cluster is mostly one theme with an adjacent provider-specific item.
Open issues1
- Issue#53455vision_analyze tool passes invalid temperature to kimi-k2.7-code model
Open PRs1
- PR#53495fix(temperature): set temperature=1 for Kimi coding models on /coding/ endpoint (#53455)
Fixes on record
-
#11834 fix(kimi): force kimi-for-coding temperature to 0.6
files:
agent/auxiliary_client.py,run_agent.py,tests/agent/test_auxiliary_client.pyShow the diff
-
agent/auxiliary_client.py@@ -94,6 +94,17 @@ def _normalize_aux_provider(provider: Optional[str]) -> str: return "custom" return _PROVIDER_ALIASES.get(normalized, normalized) + +_FIXED_TEMPERATURE_MODELS: Dict[str, float] = { + "kimi-for-coding": 0.6, +} + + +def _fixed_temperature_for_model(model: Optional[str]) -> Optional[float]: + """Return a required temperature override for models with strict contracts.""" + normalized = (model or "").strip().lower() + return _FIXED_TEMPERATURE_MODELS.get(normalized) + # Default auxiliary models for direct API-key providers (cheap/fast for side tasks) _API_KEY_PROVIDER_AUX_MODELS: [redacted:secret-assignment] str] = { "gemini": "gemini-3-flash-preview", @@ -2293,6 +2304,10 @@ def _build_call_kwargs( "timeout": timeout, } + fixed_temperature = _fixed_temperature_for_model(model) + if fixed_temperature is not None: + temperature = fixed_temperature + # Opus 4.7+ rejects any non-default temperature/top_p/top_k — silently # drop here so auxiliary callers that hardcode temperature (e.g. 0.3 on # flush_memories, 0 on structured-JSON extraction) don't 400 the moment -
run_agent.py@@ -7207,14 +7207,22 @@ def flush_memories(self, messages: list = None, min_turns: int = None): # Use auxiliary client for the flush call when available -- # it's cheaper and avoids Codex Responses API incompatibility. - from agent.auxiliary_client import call_llm as _call_llm + from agent.auxiliary_client import ( + call_llm as _call_llm, + _fixed_temperature_for_model, + ) _aux_available = True + # Use the fixed-temperature override (e.g. kimi-for-coding → 0.6) if + # the model has a strict contract; otherwise the historical 0.3 default. + _flush_temperature = _fixed_temperature_for_model(self.model) + if _flush_temperature is None: + _flush_temperature = 0.3 try: response = _call_llm( task="flush_memories", messages=api_messages, tools=[memory_tool_def], - temperature=0.3, + temperature=_flush_temperature, max_tokens=5120, # timeout resolved from auxiliary.flush_memories.timeout config ) @@ -7226,7 +7234,7 @@ def flush_memories(self, messages: list = None, min_turns: int = None): # No auxiliary client -- use the Codex Responses path directly codex_kwargs = self._build_api_kwargs(api_messages) codex_kwargs["tools"] = self._responses_tools([memory_tool_def]) - codex_kwargs["temperature"] = 0.3 + codex_kwargs["temperature"] = _flush_temperature if "max_output_tokens" in codex_kwargs: codex_kwargs["max_output_tokens"] = 5120 response = self._run_codex_stream(codex_kwargs) @@ -7245,7 +7253,7 @@ def flush_memories(self, messages: list = None, min_turns: int = None): "model": self.model, "messages": api_messages, "tools": [memory_tool_def], - "temperature": 0.3, + "temperature": _flush_temperature, **self._max_tokens_param(5120), } from agent.auxiliary_client import _get_task_timeout -
tests/agent/test_auxiliary_client.py@@ -696,6 +696,95 @@ def test_500_not_connection(self): assert _is_connection_error(err) is False +class TestKimiForCodingTemperature: + """kimi-for-coding now requires temperature=0.6 exactly.""" + + def test_build_call_kwargs_forces_fixed_temperature(self): + from agent.auxiliary_client import _build_call_kwargs + + kwargs = _build_call_kwargs( + provider="kimi-coding", + model="kimi-for-coding", + messages=[{"role": "user", "content": "hello"}], + temperature=0.3, + ) + + assert kwargs["temperature"] == 0.6 + + def test_build_call_kwargs_injects_temperature_when_missing(self): + from agent.auxiliary_client import _build_call_kwargs + + kwargs = _build_call_kwargs( + provider="kimi-coding", + model="kimi-for-coding", + messages=[{"role": "user", "content": "hello"}], + temperature=None, + ) + + assert kwargs["temperature"] == 0.6 + + def test_auto_routed_kimi_for_coding_sync_call_uses_fixed_temperature(self): + client = MagicMock() + client.base_url = "https://api.kimi.com/coding/v1" + response = MagicMock() + client.chat.completions.create.return_value = response + + with patch( + "agent.auxiliary_client._get_cached_client", + return_value=(client, "kimi-for-coding"), + ), patch( + "agent.auxiliary_client._resolve_task_provider_model", + return_value=("auto", "kimi-for-coding", None, None, None), + ): + result = call_llm( + task="session_search", + messages=[{"role": "user", "content": "hello"}], + temperature=0.1, + ) + + assert result is response + kwargs = client.chat.completions.create.call_args.kwargs + assert kwargs["model"] == "kimi-for-coding" + assert kwargs["temperature"] == 0.6 + + @pytest.mark.asyncio + async def test_auto_routed_kimi_for_coding_async_call_uses_fixed_temperature(self): + client = MagicMock() + client.base_url = "https://api.kimi.com/coding/v1" + response = MagicMock() + client.chat.completions.create = AsyncMock(return_value=response) + + with patch( + "agent.auxiliary_client._get_cached_client", + return_value=(client, "kimi-for-coding"), + ), patch( + "agent.auxiliary_client._resolve_task_provider_model", + return_value=("auto", "kimi-for-coding", None, None, None), + ): + result = await async_call_llm( + task="session_search", + messages=[{"role": "user", "content": "hello"}], + temperature=0.1, + ) + + assert result is response + kwargs = client.chat.completions.create.call_args.kwargs + assert kwargs["model"] == "kimi-for-coding" + assert kwargs["temperature"] == 0.6 + + def test_non_kimi_model_still_preserves_temperature(self): + from agent.auxiliary_client import _build_call_kwargs + + kwargs = _build_call_kwargs( + provider="kimi-coding", + model="kimi-k2.5", + messages=[{"role": "user", "content": "hello"}], + temperature=0.3, + ) + + assert kwargs["temperature"] == 0.3 + + # --------------------------------------------------------------------------- # async_call_llm payment / connection fallback (#7512 bug 2) # ---------------------------------------------------------------------------
-
-
#12773 fix(kimi): honor public Moonshot temperature contract across direct call paths
files:
agent/auxiliary_client.py,mini_swe_runner.py,run_agent.py,scripts/release.py,tests/agent/test_auxiliary_client.py,tests/run_agent/test_run_agent.py,tests/test_mini_swe_runner.py,tests/test_trajectory_compressor.py,tests/test_trajectory_compressor_async.py,trajectory_compressor.pyShow the diff
-
agent/auxiliary_client.py@@ -116,8 +116,25 @@ def _normalize_aux_provider(provider: Optional[str]) -> str: "kimi-k2-thinking-turbo", }) +# Moonshot's public chat endpoint (api.moonshot.ai/v1) enforces a different +# temperature contract than the Coding Plan endpoint above. Empirically, +# `kimi-k2.5` on the public API rejects 0.6 with HTTP 400 +# "invalid temperature: only 1 is allowed for this model" — the Coding Plan +# lock (0.6 for non-thinking) does not apply. `kimi-k2-turbo-preview` and the +# thinking variants already match the Coding Plan contract on the public +# endpoint, so we only override the models that diverge. +# Users hit this endpoint when `KIMI_API_KEY` is a legacy `sk-*` key (the +# `sk-kimi-*` prefix routes to api.kimi.com/coding/v1 instead — see +# hermes_cli/auth.py:_kimi_base_url_for_key). +_KIMI_PUBLIC_API_OVERRIDES: Dict[str, float] = { + "kimi-k2.5": 1.0, +} + -def _fixed_temperature_for_model(model: Optional[str]) -> Optional[float]: +def _fixed_temperature_for_model( + model: Optional[str], + base_url: Optional[str] = None, +) -> Optional[float]: """Return a required temperature override for models with strict contracts. Moonshot's kimi-for-coding endpoint rejects any non-approved temperature on @@ -125,15 +142,31 @@ def _fixed_temperature_for_model(model: Optional[str]) -> Optional[float]: variants require 1.0. An optional ``vendor/`` prefix (e.g. ``moonshotai/kimi-k2.5``) is tolerated for aggregator routings. + When ``base_url`` points to Moonshot's public chat endpoint + (``api.moonshot.ai``), the contract changes for ``kimi-k2.5``: the public + API only accepts ``temperature=1``, not 0.6. That override takes precedence + over the Coding Plan defaults above. + Returns ``None`` for every other model, including ``kimi-k2-instruct*`` which is the separate non-coding K2 family with variable temperature. """ normalized = (model or "").strip().lower() + bare = normalized.rsplit("/", 1)[-1] + + # Public Moonshot API has a stricter contract for some models than the + # Coding Plan endpoint — check it first so it wins on conflict. + if base_url and "api.moonshot.ai" in base_url.lower(): + public = _KIMI_PUBLIC_API_OVERRIDES.get(bare) + if public is not None: + logger.debug( + "Forcing temperature=%s for %r on public Moonshot API", public, model + ) + return public + fixed = _FIXED_TEMPERATURE_MODELS.get(normalized) if fixed is not None: logger.debug("Forcing temperature=%s for model %r (fixed map)", fixed, model) return fixed - bare = normalized.rsplit("/", 1)[-1] if bare in _KIMI_THINKING_MODELS: logger.debug("Forcing temperature=1.0 for kimi thinking model %r", model) return 1.0 @@ -2417,7 +2450,7 @@ def _build_call_kwargs( "timeout": timeout, } - fixed_temperature = _fixed_temperature_for_model(model) + fixed_temperature = _fixed_temperature_for_model(model, base_url) if fixed_temperature is not None: temperature = fixed_temperature @@ -2598,11 +2631,14 @@ def call_llm( task, resolved_provider or "auto", final_model or "default", f" at {_base_info}" if _base_info and "openrouter" not in _base_info else "") + # Pass the client's actual base_url (not just resolved_base_url) so + # endpoint-specific temperature overrides can distinguish + # api.moonshot.ai vs api.kimi.com/coding even on auto-detected routes. kwargs = _build_call_kwargs( resolved_provider, final_model, messages, temperature=temperature, max_tokens=[redacted:secret-assignment] tools=tools, timeout=effective_timeout, extra_body=extra_body, - base_url=resolved_base_url) + base_url=_base_info or resolved_base_url) # Convert image blocks for Anthropic-compatible endpoints (e.g. MiniMax) _client_base = str(getattr(client, "base_url", "") or "") @@ -2656,7 +2692,8 @@ def call_llm( fb_label, fb_model, messages, temperature=temperature, max_tokens=[redacted:secret-assignment] tools=tools, timeout=effective_timeout, - extra_body=extra_body) + extra_body=extra_body, + base_url=str(getattr(fb_client, "base_url", "") or "")) return _validate_llm_response( fb_client.chat.completions.create(**fb_kwargs), task) raise @@ -2791,14 +2828,17 @@ async def async_call_llm( effective_timeout = timeout if timeout is not None else _get_task_timeout(task) + # Pass the client's actual base_url (not just resolved_base_url) so + # endpoint-specific temperature overrides can distinguish + # api.moonshot.ai vs api.kimi.com/coding even on auto-detected routes. + _client_base = str(getattr(client, "base_url", "") or "") kwargs = _build_call_kwargs( resolved_provider, final_model, messages, temperature=temperature, max_tokens=[redacted:secret-assignment] tools=tools, timeout=effective_timeout, extra_body=extra_body, - base_url=resolved_base_url) + base_url=_client_base or resolved_base_url) # Convert image blocks for Anthropic-compatible endpoints (e.g. MiniMax) - _client_base = str(getattr(client, "base_url", "") or "") if _is_anthropic_compat_endpoint(resolved_provider, _client_base): kwargs["messages"] = _convert_openai_images_to_anthropic(kwargs["messages"]) @@ -2834,7 +2874,8 @@ async def async_call_llm( fb_label, fb_model, messages, temperature=temperature, max_tokens=[redacted:secret-assignment] tools=tools, timeout=effective_timeout, - extra_body=extra_body) + extra_body=extra_body, + base_url=str(getattr(fb_client, "base_url", "") or "")) # Convert sync fallback client to async async_fb, async_fb_model = _to_async_client(fb_client, fb_model or "") if async_fb_model and async_fb_model != fb_kwargs.get("model"): -
mini_swe_runner.py@@ -43,13 +43,16 @@ load_dotenv() -def _effective_temperature_for_model(model: str) -> Optional[float]: +def _effective_temperature_for_model( + model: str, + base_url: Optional[str] = None, +) -> Optional[float]: """Return a fixed temperature for models with strict sampling contracts.""" try: from agent.auxiliary_client import _fixed_temperature_for_model except Exception: return None - return _fixed_temperature_for_model(model) + return _fixed_temperature_for_model(model, base_url) @@ -457,7 +460,10 @@ def run_task(self, task: str) -> Dict[str, Any]: "tools": self.tools, "timeout": 300.0, } - fixed_temperature = _effective_temperature_for_model(self.model) + fixed_temperature = _effective_temperature_for_model( + self.model, + str(getattr(self.client, "base_url", "") or ""), + ) if fixed_temperature is not None: api_kwargs["temperature"] = fixed_temperature -
run_agent.py@@ -7173,7 +7173,7 @@ def _build_api_kwargs(self, api_messages: list) -> dict: except Exception: _fixed_temperature_for_model = None if _fixed_temperature_for_model is not None: - fixed_temperature = _fixed_temperature_for_model(self.model) + fixed_temperature = _fixed_temperature_for_model(self.model, self.base_url) if fixed_temperature is not None: api_kwargs["temperature"] = fixed_temperature if self._is_qwen_portal(): @@ -7619,7 +7619,7 @@ def flush_memories(self, messages: list = None, min_turns: int = None): _aux_available = True # Use the fixed-temperature override (e.g. kimi-for-coding → 0.6) if # the model has a strict contract; otherwise the historical 0.3 default. - _flush_temperature = _fixed_temperature_for_model(self.model) + _flush_temperature = _fixed_temperature_for_model(self.model, self.base_url) if _flush_temperature is None: _flush_temperature = 0.3 try: @@ -8675,7 +8675,7 @@ def _handle_max_iterations(self, messages: list, api_call_count: int) -> str: except Exception: _fixed_temperature_for_model = None _summary_temperature = ( - _fixed_temperature_for_model(self.model) + _fixed_temperature_for_model(self.model, self.base_url) if _fixed_temperature_for_model is not None else None ) -
scripts/release.py@@ -107,6 +107,7 @@ "[email protected]": "handsdiff", "[redacted:email]": "etherman-os", "[redacted:email]": "mark-ramsell", + "[redacted:email]": "taeng0204", "[redacted:email]": "gpickett00", "[redacted:email]": "wakamex", "[redacted:email]": "clawdia-nash", -
tests/agent/test_auxiliary_client.py@@ -832,6 +832,92 @@ def test_non_restricted_model_preserves_temperature(self, model): assert kwargs["temperature"] == 0.3 + # ── Endpoint-aware overrides: api.moonshot.ai vs api.kimi.com/coding ── + # The public Moonshot chat endpoint and the Coding Plan endpoint enforce + # different temperature contracts for the same model name. `kimi-k2.5` on + # api.moonshot.ai rejects 0.6 with HTTP 400 "only 1 is allowed for this + # model", while the Coding Plan docs mandate 0.6. Override must pick the + # right value per base_url. + + @pytest.mark.parametrize( + "base_url", + [ + "https://api.moonshot.ai/v1", + "https://api.moonshot.ai/v1/", + "https://API.MOONSHOT.AI/v1", + ], + ) + def test_kimi_k2_5_public_api_forces_temperature_1(self, base_url): + """kimi-k2.5 on the public Moonshot API only accepts temperature=1.""" + from agent.auxiliary_client import _build_call_kwargs + + kwargs = _build_call_kwargs( + provider="kimi-coding", + model="kimi-k2.5", + messages=[{"role": "user", "content": "hello"}], + temperature=0.1, + base_url=base_url, + ) + + assert kwargs["temperature"] == 1.0 + + def test_kimi_k2_5_coding_plan_keeps_temperature_0_6(self): + """kimi-k2.5 on api.kimi.com/coding keeps the Coding Plan's 0.6 lock.""" + from agent.auxiliary_client import _build_call_kwargs + + kwargs = _build_call_kwargs( + provider="kimi-coding", + model="kimi-k2.5", + messages=[{"role": "user", "content": "hello"}], + temperature=0.1, + base_url="https://api.kimi.com/coding/v1", + ) + + assert kwargs["temperature"] == 0.6 + + def test_kimi_k2_5_no_base_url_falls_back_to_coding_plan_lock(self): + """Without a base_url hint, the Coding Plan default (0.6) applies. + + Preserves PR #12144 backward compatibility for callers that don't thread + the client's base_url through. + """ + from agent.auxiliary_client import _build_call_kwargs + + kwargs = _build_call_kwargs( + provider="kimi-coding", + model="kimi-k2.5", + messages=[{"role": "user", "content": "hello"}], + temperature=0.1, + ) + + assert kwargs["temperature"] == 0.6 + + @pytest.mark.parametrize( + "model,expected", + [ + # Only kimi-k2.5 diverges on api.moonshot.ai; the rest keep the + # Coding Plan lock (empirically verified against Moonshot in April + # 2026: turbo-preview accepts 0.6, thinking-turbo accepts 1.0). + ("kimi-k2-turbo-preview", 0.6), + ("kimi-k2-0905-preview", 0.6), + ("kimi-k2-thinking", 1.0), + ("kimi-k2-thinking-turbo", 1.0), + ("moonshotai/kimi-k2-thinking-turbo", 1.0), + ], + ) + def test_other_kimi_k2_family_unchanged_on_public_api(self, model, expected): + from agent.auxiliary_client import _build_call_kwargs + + kwargs = _build_call_kwargs( + provider="kimi-coding", + model=model, + messages=[{"role": "user", "content": "hello"}], + temperature=0.1, + base_url="https://api.moonshot.ai/v1", + ) + + assert kwargs["temperature"] == expected + # --------------------------------------------------------------------------- # async_call_llm payment / connection fallback (#7512 bug 2) -
tests/run_agent/test_run_agent.py@@ -918,6 +918,26 @@ def test_basic_kwargs(self, agent): assert kwargs["messages"] is messages assert kwargs["timeout"] == 1800.0 + def test_public_moonshot_kimi_k2_5_forces_temperature_1(self, agent): + agent.base_url = "https://api.moonshot.ai/v1" + agent._base_url_lower = agent.base_url.lower() + agent.model = "kimi-k2.5" + messages = [{"role": "user", "content": "hi"}] + + kwargs = agent._build_api_kwargs(messages) + + assert kwargs["temperature"] == 1.0 + + def test_kimi_coding_endpoint_keeps_kimi_k2_5_at_0_6(self, agent): + agent.base_url = "https://api.kimi.com/coding/v1" + agent._base_url_lower = agent.base_url.lower() + agent.model = "kimi-k2.5" + messages = [{"role": "user", "content": "hi"}] + + kwargs = agent._build_api_kwargs(messages) + + assert kwargs["temperature"] == 0.6 + def test_provider_preferences_injected(self, agent): agent.base_url = "https://openrouter.ai/api/v1" agent.providers_allowed = ["Anthropic"] -
tests/test_mini_swe_runner.py@@ -26,3 +26,30 @@ def test_run_task_forces_kimi_fixed_temperature(): assert result["completed"] is True assert client.chat.completions.create.call_args.kwargs["temperature"] == 0.6 + + +def test_run_task_public_moonshot_kimi_k2_5_forces_temperature_1(): + with patch("openai.OpenAI") as mock_openai: + client = MagicMock() + client.base_url = "https://api.moonshot.ai/v1" + client.chat.completions.create.return_value = SimpleNamespace( + choices=[SimpleNamespace(message=SimpleNamespace(content="done", tool_calls=[]))] + ) + mock_openai.return_value = client + + from mini_swe_runner import MiniSWERunner + + runner = MiniSWERunner( + model="kimi-k2.5", + base_url="https://api.moonshot.ai/v1", + api_key="[redacted:secret-assignment]", + env_type="local", + max_iterations=1, + ) + runner._create_env = MagicMock() + runner._cleanup_env = MagicMock() + + result = runner.run_task("2+2") + + assert result["completed"] is True + assert client.chat.completions.create.call_args.kwargs["temperature"] == 1.0 -
tests/test_trajectory_compressor.py@@ -54,6 +54,30 @@ def test_generate_summary_custom_client_forces_kimi_temperature(): assert compressor.client.chat.completions.create.call_args.kwargs["temperature"] == 0.6 +def test_generate_summary_public_moonshot_kimi_k2_5_forces_temperature_1(): + config = CompressionConfig( + summarization_model="kimi-k2.5", + base_url="https://api.moonshot.ai/v1", + temperature=0.3, + summary_target_tokens=100, + max_retries=1, + ) + compressor = TrajectoryCompressor.__new__(TrajectoryCompressor) + compressor.config = config + compressor.logger = MagicMock() + compressor._use_call_llm = False + compressor.client = MagicMock() + compressor.client.chat.completions.create.return_value = SimpleNamespace( + choices=[SimpleNamespace(message=SimpleNamespace(content="[CONTEXT SUMMARY]: summary"))] + ) + + metrics = TrajectoryMetrics() + result = compressor._generate_summary("tool output", metrics) + + assert result.startswith("[CONTEXT SUMMARY]:") + assert compressor.client.chat.completions.create.call_args.kwargs["temperature"] == 1.0 + + # --------------------------------------------------------------------------- # CompressionConfig # --------------------------------------------------------------------------- -
tests/test_trajectory_compressor_async.py@@ -141,3 +141,31 @@ async def test_generate_summary_async_custom_client_forces_kimi_temperature(): assert result.startswith("[CONTEXT SUMMARY]:") assert async_client.chat.completions.create.call_args.kwargs["temperature"] == 0.6 + + [redacted:email] +async def test_generate_summary_async_public_moonshot_kimi_k2_5_forces_temperature_1(): + from trajectory_compressor import CompressionConfig, TrajectoryCompressor, TrajectoryMetrics + + config = CompressionConfig( + summarization_model="kimi-k2.5", + base_url="https://api.moonshot.ai/v1", + temperature=0.3, + summary_target_tokens=100, + max_retries=1, + ) + compressor = TrajectoryCompressor.__new__(TrajectoryCompressor) + compressor.config = config + compressor.logger = MagicMock() + compressor._use_call_llm = False + async_client = MagicMock() + async_client.chat.completions.create = MagicMock(return_value=SimpleNamespace( + choices=[SimpleNamespace(message=SimpleNamespace(content="[CONTEXT SUMMARY]: summary"))] + )) + compressor._get_async_client = MagicMock(return_value=async_client) + + metrics = TrajectoryMetrics() + result = await compressor._generate_summary_async("tool output", metrics) + + assert result.startswith("[CONTEXT SUMMARY]:") + assert async_client.chat.completions.create.call_args.kwargs["temperature"] == 1.0 -
trajectory_compressor.py@@ -54,14 +54,18 @@ load_hermes_dotenv(hermes_home=_hermes_home, project_env=_project_env) -def _effective_temperature_for_model(model: str, requested_temperature: float) -> float: +def _effective_temperature_for_model( + model: str, + requested_temperature: float, + base_url: Optional[str] = None, +) -> float: """Apply fixed model temperature contracts to direct client calls.""" try: from agent.auxiliary_client import _fixed_temperature_for_model except Exception: return requested_temperature - fixed_temperature = _fixed_temperature_for_model(model) + fixed_temperature = _fixed_temperature_for_model(model, base_url) if fixed_temperature is not None: return fixed_temperature return requested_temperature @@ -583,6 +587,7 @@ def _generate_summary(self, content: str, metrics: TrajectoryMetrics) -> str: summary_temperature = _effective_temperature_for_model( self.config.summarization_model, self.config.temperature, + self.config.base_url, ) if getattr(self, '_use_call_llm', False): @@ -649,6 +654,7 @@ async def _generate_summary_async(self, content: str, metrics: TrajectoryMetrics summary_temperature = _effective_temperature_for_model( self.config.summarization_model, self.config.temperature, + self.config.base_url, ) if getattr(self, '_use_call_llm', False):
-
-
#12861 fix(kimi): include api.moonshot.cn in public temperature override
files:
agent/auxiliary_client.py,tests/agent/test_auxiliary_client.py,tests/run_agent/test_run_agent.py,tests/test_trajectory_compressor.py,tests/test_trajectory_compressor_async.pyShow the diff
-
agent/auxiliary_client.py@@ -155,7 +155,7 @@ def _fixed_temperature_for_model( # Public Moonshot API has a stricter contract for some models than the # Coding Plan endpoint — check it first so it wins on conflict. - if base_url and "api.moonshot.ai" in base_url.lower(): + if base_url and ("api.moonshot.ai" in base_url.lower() or "api.moonshot.cn" in base_url.lower()): public = _KIMI_PUBLIC_API_OVERRIDES.get(bare) if public is not None: logger.debug( -
tests/agent/test_auxiliary_client.py@@ -845,6 +845,8 @@ def test_non_restricted_model_preserves_temperature(self, model): "https://api.moonshot.ai/v1", "https://api.moonshot.ai/v1/", "https://API.MOONSHOT.AI/v1", + "https://api.moonshot.cn/v1", + "https://api.moonshot.cn/v1/", ], ) def test_kimi_k2_5_public_api_forces_temperature_1(self, base_url): -
tests/run_agent/test_run_agent.py@@ -928,6 +928,16 @@ def test_public_moonshot_kimi_k2_5_forces_temperature_1(self, agent): assert kwargs["temperature"] == 1.0 + def test_public_moonshot_cn_kimi_k2_5_forces_temperature_1(self, agent): + agent.base_url = "https://api.moonshot.cn/v1" + agent._base_url_lower = agent.base_url.lower() + agent.model = "kimi-k2.5" + messages = [{"role": "user", "content": "hi"}] + + kwargs = agent._build_api_kwargs(messages) + + assert kwargs["temperature"] == 1.0 + def test_kimi_coding_endpoint_keeps_kimi_k2_5_at_0_6(self, agent): agent.base_url = "https://api.kimi.com/coding/v1" agent._base_url_lower = agent.base_url.lower() -
tests/test_trajectory_compressor.py@@ -78,6 +78,30 @@ def test_generate_summary_public_moonshot_kimi_k2_5_forces_temperature_1(): assert compressor.client.chat.completions.create.call_args.kwargs["temperature"] == 1.0 +def test_generate_summary_public_moonshot_cn_kimi_k2_5_forces_temperature_1(): + config = CompressionConfig( + summarization_model="kimi-k2.5", + base_url="https://api.moonshot.cn/v1", + temperature=0.3, + summary_target_tokens=100, + max_retries=1, + ) + compressor = TrajectoryCompressor.__new__(TrajectoryCompressor) + compressor.config = config + compressor.logger = MagicMock() + compressor._use_call_llm = False + compressor.client = MagicMock() + compressor.client.chat.completions.create.return_value = SimpleNamespace( + choices=[SimpleNamespace(message=SimpleNamespace(content="[CONTEXT SUMMARY]: summary"))] + ) + + metrics = TrajectoryMetrics() + result = compressor._generate_summary("tool output", metrics) + + assert result.startswith("[CONTEXT SUMMARY]:") + assert compressor.client.chat.completions.create.call_args.kwargs["temperature"] == 1.0 + + # --------------------------------------------------------------------------- # CompressionConfig # --------------------------------------------------------------------------- -
tests/test_trajectory_compressor_async.py@@ -169,3 +169,32 @@ async def test_generate_summary_async_public_moonshot_kimi_k2_5_forces_temperatu assert result.startswith("[CONTEXT SUMMARY]:") assert async_client.chat.completions.create.call_args.kwargs["temperature"] == 1.0 + + + [redacted:email] +async def test_generate_summary_async_public_moonshot_cn_kimi_k2_5_forces_temperature_1(): + from trajectory_compressor import CompressionConfig, TrajectoryCompressor, TrajectoryMetrics + + config = CompressionConfig( + summarization_model="kimi-k2.5", + base_url="https://api.moonshot.cn/v1", + temperature=0.3, + summary_target_tokens=100, + max_retries=1, + ) + compressor = TrajectoryCompressor.__new__(TrajectoryCompressor) + compressor.config = config + compressor.logger = MagicMock() + compressor._use_call_llm = False + async_client = MagicMock() + async_client.chat.completions.create = MagicMock(return_value=SimpleNamespace( + choices=[SimpleNamespace(message=SimpleNamespace(content="[CONTEXT SUMMARY]: summary"))] + )) + compressor._get_async_client = MagicMock(return_value=async_client) + + metrics = TrajectoryMetrics() + result = await compressor._generate_summary_async("tool output", metrics) + + assert result.startswith("[CONTEXT SUMMARY]:") + assert async_client.chat.completions.create.call_args.kwargs["temperature"] == 1.0
-
-
#12862 fix(vision): honor auxiliary.vision.temperature from config
files:
scripts/release.py,tests/tools/test_browser_camofox.py,tests/tools/test_browser_console.py,tests/tools/test_vision_tools.py,tools/browser_camofox.py,tools/browser_tool.py,tools/vision_tools.py -
#115850 fix(auxiliary): vision and other auxiliary calls to gpt-5.x / o-series no longer pay a temperature 400 round-trip (#51083, salvage #51157)
files:
agent/auxiliary_client.py,tests/agent/test_auxiliary_client.py,tests/agent/test_auxiliary_parameter_rung_chaining.py,tests/agent/test_injected_param_strip_retry_registry.py,tests/agent/test_unsupported_temperature_retry.py
9 PRs closed without merging
From the close comments: merged via another change 4 · stale or abandoned 2 · done elsewhere 1 · superseded by another PR 1 · other 1
-
#4967 fix: vision tool respects auxiliary.vision.temperature from config · done elsewhere →
088bf90,#4661 -
#9191 fix(auxiliary): omit temperature for auto-routed kimi calls · stale or abandoned →
#9125 -
#11825 fix(kimi): force kimi-for-coding temperature to 0.6 · merged via another change →
#11834 -
#12668 fix(kimi): kimi-k2.5 needs temperature=1.0 on api.moonshot.ai endpoint · merged via another change →
#12773 -
#12680 fix(kimi): honor public Moonshot temperature contract across direct call paths · merged via another change →
#12773
Show 4 more
-
#12760 fix: move kimi-k2.5 from instant to thinking models · superseded by another PR →
#12773 - #12772 fix: move kimi-k2.5 to thinking models · stale or abandoned
- #12806 fix: include api.moonshot.cn in public API temperature override (#12745) · other
-
#51157 fix(auxiliary): OMIT temperature for gpt-5.5 family (#51083) · merged via another change →
ada97e3,#115850,#51083,#51157
Closed issues · 5
- Closed#4661[Bug]: kimi-coding vision provider fails with 400 error on second call due to hardcoded temperature
- Closed#9125[Bug]: kimi-coding auxiliary calls fail with 400 "invalid temperature" when provider=auto
- Closed#12404kimi-k2.5 fails with invalid temperature error
- Closed#12745[Bug]: kimi-k2.5 incorrectly classified as instant mode (temperature=0.6) instead of thinking mode (temperature=1.0)
- Closed#51083[Bug]: auxiliary vision sends unsupported temperature to OpenAI gpt-5.5
How this cluster was grouped
-
Link evidence (
partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text. - Members outside that structural core may belong to a different underlying problem.
-
Pairwise link density
0.09: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36.1.00means every pair is linked.
Pick up a Lab task
Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.
Open the Lab