diff --git a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/knowledge_store_persistence/commit_message.py b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/knowledge_store_persistence/commit_message.py index 4dd442b42..ed6897f39 100644 --- a/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/knowledge_store_persistence/commit_message.py +++ b/surfsense_backend/app/agents/chat/multi_agent_chat/main_agent/middleware/knowledge_store_persistence/commit_message.py @@ -7,6 +7,8 @@ import logging from collections.abc import Iterable, Mapping from typing import Any +from app.tasks.chat.llm_history_normalizer import assistant_content_to_llm_text + logger = logging.getLogger(__name__) _SYSTEM_PROMPT = ( @@ -71,9 +73,9 @@ async def generate_commit_message( ), timeout=_GENERATION_TIMEOUT_SECONDS, ) - content = getattr(reply, "content", "") - if not isinstance(content, str): - content = str(content) + # A reasoning model answers in content blocks, so the subject has to be + # read out of them: ``str()`` on the list commits the model's thinking. + content = assistant_content_to_llm_text(getattr(reply, "content", "")) subject = content.strip().splitlines()[0].strip() if content.strip() else "" if subject: return subject diff --git a/surfsense_backend/tests/unit/middleware/test_commit_message.py b/surfsense_backend/tests/unit/middleware/test_commit_message.py index 6f133fabe..4745dcd4d 100644 --- a/surfsense_backend/tests/unit/middleware/test_commit_message.py +++ b/surfsense_backend/tests/unit/middleware/test_commit_message.py @@ -50,6 +50,25 @@ async def test_uses_the_models_reply_as_subject(): assert message == "docs: add meeting notes" +async def test_a_reasoning_models_thinking_never_reaches_the_subject(): + """Reasoning models answer in blocks; the shape below is a real reply.""" + + class _ReasoningModel: + async def ainvoke(self, _input, config=None, **kwargs): + return SimpleNamespace( + content=[ + {"type": "thinking", "thinking": "**Inferring commit message**"}, + {"type": "thinking", "thinking": " the user wants a leaf image"}, + "docs: add simple green leaf image prompt", + ] + ) + + message = await generate_commit_message( + _ReasoningModel(), writes={"documents/leaf.md": b"# Leaf"}, removes=[] + ) + assert message == "docs: add simple green leaf image prompt" + + async def test_falls_back_deterministically_when_the_model_fails(): message = await generate_commit_message( _BrokenModel(),