Closes #1357
Mistral's reasoning models (and some other OpenAI-compatible servers) send message.content as a list of blocks instead of a string, both in full responses and in every stream delta:
[{"type": "thinking", "thinking": [{"type": "text", "text": "..."}]}, {"type": "text", "text": "OK"}]The SDK's chat-completions converter assumes a string, so the first call dies with a ResponseOutputText (or ResponseTextDeltaEvent when streaming) validation error and Strix reports LLM CONNECTION FAILED.
This flattens that content before the SDK reads it. StrixProvider.get_model now returns BlockTolerantChatCompletionsModel (new strix/llm/content_blocks.py) in place of the stock OpenAIChatCompletionsModel. It overrides _fetch_response, so streaming and non-streaming are handled in one spot. Text blocks become the message text, thinking blocks go to reasoning_content so the SDK keeps them as a reasoning item, and unknown block types are skipped. String content is untouched, and the LiteLLM route and custom subclasses are left alone.
I went with a subclass rather than patching chatcmpl_converter so nothing changes globally, and it's easy to drop if the SDK starts handling this itself.
tests/test_content_blocks.py runs against a local server that replies the way Mistral does. It checks that the stock model still fails (so the test really exercises the bug), that both paths return the text plus a reasoning item, that a thinking-only turn keeps its tool call, that string content is unchanged, and that the provider wiring is in place. ruff and mypy are clean, and the rest of the suite has the same failures here as on main.
[Medium impact] The change reaches Strix’s regular chat-completions model calls.
The PR appears safe to merge, with a non-blocking fix recommended for test isolation.
Findings
Fix with agent prompt
Summary
Adds a chat-completions model that flattens list-shaped response content before the SDK converts it.
Reviews (1) · Last reviewed commit: "test: cover list-shaped message content" · Reviewed by Greptile