diff --git a/hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.py b/hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.py index d72091c3df..cade3be8f7 100644 --- a/hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.py +++ b/hindsight-api-slim/hindsight_api/engine/consolidation/consolidator.py @@ -303,6 +303,7 @@ async def _dedup_adjudicate( await dedup_llm_config.call( messages=[{"role": "user", "content": _DEDUP_PROMPT.format(new=anchor_text, existing=best_text)}], response_format=_DedupDecision, + temperature=config.llm_temperature_consolidation, scope="consolidation_dedup", strict_schema=get_config().llm_strict_schema_consolidation, ) @@ -2845,6 +2846,7 @@ def _fact_line(m: dict[str, Any]) -> str: {"role": "user", "content": user_content}, ], "response_format": response_model, + "temperature": config.llm_temperature_consolidation, "scope": "consolidation", # Resolved per operation (HINDSIGHT_API_LLM_STRICT_SCHEMA_CONSOLIDATION, falling # back to the global flag) so an operator can grammar-enforce consolidation's diff --git a/hindsight-api-slim/tests/test_consolidation.py b/hindsight-api-slim/tests/test_consolidation.py index 61f5d04f82..2c4de3effe 100644 --- a/hindsight-api-slim/tests/test_consolidation.py +++ b/hindsight-api-slim/tests/test_consolidation.py @@ -2530,6 +2530,7 @@ async def test_unsupported_max_items_still_truncates_creates( consolidation_max_attempts=1, consolidation_llm_max_retries=None, consolidation_max_completion_tokens=None, + llm_temperature_consolidation=0.0, ) result = await _consolidate_batch_with_llm( diff --git a/hindsight-api-slim/tests/test_consolidation_dedup.py b/hindsight-api-slim/tests/test_consolidation_dedup.py index 2a240ca802..c45c63f659 100644 --- a/hindsight-api-slim/tests/test_consolidation_dedup.py +++ b/hindsight-api-slim/tests/test_consolidation_dedup.py @@ -188,6 +188,7 @@ def _ctx(threshold: float = 0.97): # config, so these must be present (production defaults: native/english). config=types.SimpleNamespace( consolidation_dedup_threshold=threshold, + llm_temperature_consolidation=0.0, text_search_extension="native", text_search_extension_native_language="english", ), @@ -229,6 +230,7 @@ async def test_dedup_llm_keep_does_not_merge() -> None: result = await _dedup_reconcile_create(**kwargs) assert result is None llm.call.assert_awaited_once() + assert llm.call.await_args.kwargs["temperature"] == 0.0 conn.fetchval.assert_not_called() # kept distinct → no merge @@ -389,6 +391,7 @@ def _update_ctx(threshold: float = 0.97): # config, so these must be present (production defaults: native/english). config=types.SimpleNamespace( consolidation_dedup_threshold=threshold, + llm_temperature_consolidation=0.0, text_search_extension="native", text_search_extension_native_language="english", ), diff --git a/hindsight-api-slim/tests/test_consolidation_output_language.py b/hindsight-api-slim/tests/test_consolidation_output_language.py index 93254b8261..4e0c805571 100644 --- a/hindsight-api-slim/tests/test_consolidation_output_language.py +++ b/hindsight-api-slim/tests/test_consolidation_output_language.py @@ -60,6 +60,7 @@ def _batch_config(llm_output_language: str | None) -> SimpleNamespace: consolidation_max_attempts=2, consolidation_llm_max_retries=None, consolidation_max_completion_tokens=None, + llm_temperature_consolidation=0.0, ) diff --git a/hindsight-api-slim/tests/test_consolidation_retry_budget.py b/hindsight-api-slim/tests/test_consolidation_retry_budget.py index a428676fbd..f0f239454d 100644 --- a/hindsight-api-slim/tests/test_consolidation_retry_budget.py +++ b/hindsight-api-slim/tests/test_consolidation_retry_budget.py @@ -26,6 +26,7 @@ def mock_config(): config.consolidation_llm_max_retries = None config.consolidation_max_completion_tokens = None config.llm_strict_schema_consolidation = False + config.llm_temperature_consolidation = 0.0 return config @@ -83,6 +84,19 @@ async def test_strict_schema_threaded_to_call(self, mock_llm_config, mock_config ) assert mock_llm_config.call.call_args.kwargs.get("strict_schema") is True + @pytest.mark.asyncio + async def test_temperature_threaded_to_call(self, mock_llm_config, mock_config): + """llm_temperature_consolidation is passed to llm_config.call().""" + mock_config.llm_temperature_consolidation = 0.65 + await _consolidate_batch_with_llm( + llm_config=mock_llm_config, + memories=[{"id": "m1", "text": "test"}], + union_observations=[], + union_source_facts={}, + config=mock_config, + ) + assert mock_llm_config.call.call_args.kwargs.get("temperature") == 0.65 + @pytest.mark.asyncio async def test_strict_schema_passed_as_explicit_false(self, mock_llm_config, mock_config): """A disabled per-operation flag is passed explicitly, not omitted.