#1663

fix(llm): accept list-shaped message content from chat completions

Opengoofisdeadwants to merge intofrom

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.

Activity

7
greptile-apps[bot]2d ago

Confidence Score: 4/5

[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

  1.  Tests inherit fake settings ▶
Fix with agent prompt
### Issue 1
tests/test_content_blocks.py:288-295
This provider test leaves shared settings changed after it finishes. `configure_sdk_model_defaults(Settings())` sets `litellm.api_key`, `litellm.api_base`, and the SDK's default API route, but the test restores only the SDK client/key and environment variables. Later tests can inherit the fake Mistral endpoint or chat-completions route, making results depend on test order. Save and restore these globals with `monkeypatch`, as the existing provider tests do.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

Adds a chat-completions model that flattens list-shaped response content before the SDK converts it.

  • Chat replies now turn content blocks into text and reasoning.

Reviews (1) · Last reviewed commit: "test: cover list-shaped message content" · Reviewed by Greptile

greptile-apps[bot]reviewed2d ago
goofisdeadreviewed2d ago