fix(state): include finish_reason in conversation replay
SELECT in get_messages_as_conversation() was missing finish_reason, so assistant messages round-tripped through replay (including /branch copies) silently dropped the provider's stop signal. Adds it to the SELECT, restores it on assistant rows, and locks it in with a round-trip test.
This commit is contained in:
parent
7ba1a2b3df
commit
a94841eaa0
@ -1464,8 +1464,8 @@ class SessionDB:
|
|||||||
placeholders = ",".join("?" for _ in session_ids)
|
placeholders = ",".join("?" for _ in session_ids)
|
||||||
rows = self._conn.execute(
|
rows = self._conn.execute(
|
||||||
"SELECT role, content, tool_call_id, tool_calls, tool_name, "
|
"SELECT role, content, tool_call_id, tool_calls, tool_name, "
|
||||||
"reasoning, reasoning_content, reasoning_details, codex_reasoning_items, "
|
"finish_reason, reasoning, reasoning_content, reasoning_details, "
|
||||||
"codex_message_items "
|
"codex_reasoning_items, codex_message_items "
|
||||||
f"FROM messages WHERE session_id IN ({placeholders}) ORDER BY timestamp, id",
|
f"FROM messages WHERE session_id IN ({placeholders}) ORDER BY timestamp, id",
|
||||||
tuple(session_ids),
|
tuple(session_ids),
|
||||||
).fetchall()
|
).fetchall()
|
||||||
@ -1490,6 +1490,8 @@ class SessionDB:
|
|||||||
# that replay reasoning (OpenRouter, OpenAI, Nous) receive
|
# that replay reasoning (OpenRouter, OpenAI, Nous) receive
|
||||||
# coherent multi-turn reasoning context.
|
# coherent multi-turn reasoning context.
|
||||||
if row["role"] == "assistant":
|
if row["role"] == "assistant":
|
||||||
|
if row["finish_reason"]:
|
||||||
|
msg["finish_reason"] = row["finish_reason"]
|
||||||
if row["reasoning"]:
|
if row["reasoning"]:
|
||||||
msg["reasoning"] = row["reasoning"]
|
msg["reasoning"] = row["reasoning"]
|
||||||
if row["reasoning_content"] is not None:
|
if row["reasoning_content"] is not None:
|
||||||
|
|||||||
@ -399,6 +399,27 @@ class TestMessageStorage:
|
|||||||
assert msg["reasoning"] == "Thinking about what to say"
|
assert msg["reasoning"] == "Thinking about what to say"
|
||||||
assert msg["reasoning_details"] == details
|
assert msg["reasoning_details"] == details
|
||||||
|
|
||||||
|
def test_finish_reason_restored_by_get_messages_as_conversation(self, db):
|
||||||
|
"""finish_reason on assistant messages must survive conversation replay.
|
||||||
|
|
||||||
|
Without this, /branch copies and other transcript round-trips silently
|
||||||
|
drop the provider's stop signal.
|
||||||
|
"""
|
||||||
|
db.create_session(session_id="s1", source="cli")
|
||||||
|
db.append_message(
|
||||||
|
"s1",
|
||||||
|
role="assistant",
|
||||||
|
content="Done",
|
||||||
|
finish_reason="tool_calls",
|
||||||
|
)
|
||||||
|
db.append_message("s1", role="user", content="next")
|
||||||
|
|
||||||
|
conv = db.get_messages_as_conversation("s1")
|
||||||
|
assert conv[0]["role"] == "assistant"
|
||||||
|
assert conv[0]["finish_reason"] == "tool_calls"
|
||||||
|
# Non-assistant rows should not have a finish_reason key added.
|
||||||
|
assert "finish_reason" not in conv[1]
|
||||||
|
|
||||||
def test_reasoning_content_persisted_and_restored(self, db):
|
def test_reasoning_content_persisted_and_restored(self, db):
|
||||||
"""reasoning_content must survive session replay as its own field."""
|
"""reasoning_content must survive session replay as its own field."""
|
||||||
db.create_session(session_id="s1", source="cli")
|
db.create_session(session_id="s1", source="cli")
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user