fix: Skip empty AI messages in output_langchain to prevent API rejection

Empty or whitespace-only AI messages cause API validation errors with strict providers (OpenAI, Z.ai, GLM): "Assistant messages must have either content or tool_calls" This is a companion fix to PR #927 (response.py KeyError fix). When that fix returns an empty string instead of crashing, the empty string can become an empty AIMessage that fails API validation. Changes: - Pre-compute content before creating message objects - Skip AI messages where content is None, empty, or whitespace-only - Human messages are not filtered (they can be empty per spec) Tested on 4 production A0 instances.

Frank Hurt committed Feb 1, 2026 at 03:26 UTC a129a576d759b75998103e77f9465fc9342ce56d
1 file changed +7 -4
python/helpers/history.py
+7 -4
@@ -519,12 +519,15 @@ def group_messages_abab(messages: list[BaseMessage]) -> list[BaseMessage]:
519 def output_langchain(messages: list[OutputMessage]):
520 result = []
521 for m in messages:
522 + content = _output_content_langchain(content=m["content"])
523 + # Skip AI messages with empty/whitespace-only content
524 + # (API spec requires assistant messages to have content or tool_calls)
525 if m["ai"]:
523 - # result.append(AIMessage(content=serialize_content(m["content"])))
524 - result.append(AIMessage(_output_content_langchain(content=m["content"]))) # type: ignore
526 + if not content or (isinstance(content, str) and not content.strip()):
527 + continue
528 + result.append(AIMessage(content)) # type: ignore
529 else:
526 - # result.append(HumanMessage(content=serialize_content(m["content"])))
527 - result.append(HumanMessage(_output_content_langchain(content=m["content"]))) # type: ignore
530 + result.append(HumanMessage(content)) # type: ignore
531 # ensure message type alternation
532 result = group_messages_abab(result)
533 return result