Fix assistant-first provider history
Omit orphaned leading assistant messages from model history so Claude-compatible providers receive a user-first conversation. Add regression coverage for the initial UI greeting path.
Alessandro committed
Aug 12, 2026 at 06:33 UTC
517a3cd90bb968314b1d0da336c286bf608e9b02
3 files changed
+17
helpers/history.py
+2
@@ -724,6 +724,8 @@ def output_langchain(messages: list[OutputMessage]):
724
result.append(HumanMessage(content)) # type: ignore
725
# ensure message type alternation
726
result = group_messages_abab(result)
727
+ while result and isinstance(result[0], AIMessage):
728
+ result.pop(0)
729
return result
730
731
helpers/history.py.dox.md
+1
@@ -80,6 +80,7 @@
80
- Update this file whenever public functions, classes, persistence behavior, path/security assumptions, side effects, or cross-module contracts change.
81
- `clear_responses_provider_state(agent)` removes the active provider continuation IDs after local history rewrites while preserving stored response ID lists for later cleanup.
82
- `Message.from_dict()` normalizes legacy AI Responses metadata through `LLMResult.metadata()` so loaded chats shed transient payloads while unrelated metadata and non-AI tool-result inputs remain intact.
83
+- `output_langchain()` removes leading assistant messages after grouping so provider histories always begin with a user turn; the WebUI greeting remains persisted and displayed but is not sent as an orphaned assistant message.
84
- Observed side-effect areas: filesystem writes, filesystem deletion, model calls, plugin state, settings/state persistence, secret handling.
85
- Imported dependency areas include: `abc`, `asyncio`, `collections`, `collections.abc`, `enum`, `helpers`, `json`, `langchain_core.messages`, `math`, `plugins._model_config.helpers.model_config`, `typing`, `uuid`.
86
tests/test_history.py
new
+14
@@ -0,0 +1,14 @@
1
+from helpers.history import output_langchain
2
+from langchain_core.messages import AIMessage, HumanMessage
3
+
4
+
5
+def test_output_langchain_omits_leading_assistant_messages():
6
+ messages = output_langchain(
7
+ [
8
+ {"ai": True, "content": "Welcome"},
9
+ {"ai": False, "content": "Hello"},
10
+ {"ai": True, "content": "Hi"},
11
+ ]
12
+ )
13
+
14
+ assert messages == [HumanMessage("Hello"), AIMessage("Hi")]