From 64e0183b55dab4319f7f8b4f43696d840bee03a6 Mon Sep 17 00:00:00 2001 From: Stable Genius Date: Sun, 15 Mar 2026 07:51:52 -0700 Subject: [PATCH] fix: drop Groq reasoning_content from assistant history (#6065) Co-authored-by: Stable Genius <259448942+stablegenius49@users.noreply.github.com> --- astrbot/core/provider/sources/groq_source.py | 8 +++ tests/test_openai_source.py | 67 ++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/astrbot/core/provider/sources/groq_source.py b/astrbot/core/provider/sources/groq_source.py index fcc8f238f..af4029f67 100644 --- a/astrbot/core/provider/sources/groq_source.py +++ b/astrbot/core/provider/sources/groq_source.py @@ -13,3 +13,11 @@ class ProviderGroq(ProviderOpenAIOfficial): ) -> None: super().__init__(provider_config, provider_settings) self.reasoning_key = "reasoning" + + def _finally_convert_payload(self, payloads: dict) -> None: + """Groq rejects assistant history items that include reasoning_content.""" + super()._finally_convert_payload(payloads) + for message in payloads.get("messages", []): + if message.get("role") == "assistant": + message.pop("reasoning_content", None) + message.pop("reasoning", None) diff --git a/tests/test_openai_source.py b/tests/test_openai_source.py index 3172097c7..2eea15d10 100644 --- a/tests/test_openai_source.py +++ b/tests/test_openai_source.py @@ -2,6 +2,7 @@ from types import SimpleNamespace import pytest +from astrbot.core.provider.sources.groq_source import ProviderGroq from astrbot.core.provider.sources.openai_source import ProviderOpenAIOfficial @@ -32,6 +33,21 @@ def _make_provider(overrides: dict | None = None) -> ProviderOpenAIOfficial: ) +def _make_groq_provider(overrides: dict | None = None) -> ProviderGroq: + provider_config = { + "id": "test-groq", + "type": "groq_chat_completion", + "model": "qwen/qwen3-32b", + "key": ["test-key"], + } + if overrides: + provider_config.update(overrides) + return ProviderGroq( + provider_config=provider_config, + provider_settings={}, + ) + + @pytest.mark.asyncio async def test_handle_api_error_content_moderated_removes_images(): provider = _make_provider( @@ -198,6 +214,57 @@ def test_extract_error_text_candidates_truncates_long_response_text(): ) +@pytest.mark.asyncio +async def test_openai_payload_keeps_reasoning_content_in_assistant_history(): + provider = _make_provider() + try: + payloads = { + "messages": [ + { + "role": "assistant", + "content": [ + {"type": "think", "think": "step 1"}, + {"type": "text", "text": "final answer"}, + ], + } + ] + } + + provider._finally_convert_payload(payloads) + + assistant_message = payloads["messages"][0] + assert assistant_message["content"] == [{"type": "text", "text": "final answer"}] + assert assistant_message["reasoning_content"] == "step 1" + finally: + await provider.terminate() + + +@pytest.mark.asyncio +async def test_groq_payload_drops_reasoning_content_from_assistant_history(): + provider = _make_groq_provider() + try: + payloads = { + "messages": [ + { + "role": "assistant", + "content": [ + {"type": "think", "think": "step 1"}, + {"type": "text", "text": "final answer"}, + ], + } + ] + } + + provider._finally_convert_payload(payloads) + + assistant_message = payloads["messages"][0] + assert assistant_message["content"] == [{"type": "text", "text": "final answer"}] + assert "reasoning_content" not in assistant_message + assert "reasoning" not in assistant_message + finally: + await provider.terminate() + + @pytest.mark.asyncio async def test_handle_api_error_content_moderated_without_images_raises(): provider = _make_provider(