fix(knowledge-store): read the commit subject out of content blocks

A reasoning model replies with a list of blocks rather than a string, so
stringifying it wrote the model's entire thinking into the revision's
commit message. Reuse the existing extractor, which keeps text and drops
reasoning.
This commit is contained in:
CREDO23 2026-08-13 15:54:15 +02:00
parent 29c9333d76
commit 008c03c2bb
2 changed files with 24 additions and 3 deletions

View file

@ -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

View file

@ -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(),