From 18760153a29f4f107d95d9405bd4d8ccb0e3e036 Mon Sep 17 00:00:00 2001 From: Roovelrz <610626596@qq.com> Date: Tue, 18 Aug 2026 16:59:44 +0800 Subject: [PATCH] fix: sanitize reasoning items before Responses replay --- .../sources/openai_responses_source.py | 15 ++++- tests/test_openai_responses_source.py | 65 ++++++++++++++++++- 2 files changed, 78 insertions(+), 2 deletions(-) diff --git a/astrbot/core/provider/sources/openai_responses_source.py b/astrbot/core/provider/sources/openai_responses_source.py index c5cb9bdb82..be08635353 100644 --- a/astrbot/core/provider/sources/openai_responses_source.py +++ b/astrbot/core/provider/sources/openai_responses_source.py @@ -27,6 +27,15 @@ class ProviderOpenAIResponses(ProviderOpenAIOfficial): """OpenAI-compatible stateless Responses API provider adapter.""" _REASONING_STATE_TYPE = "openai_responses_reasoning" + _REASONING_REPLAY_SAFE_KEYS = frozenset( + { + "type", + "id", + "summary", + "content", + "encrypted_content", + } + ) def __init__(self, provider_config: dict, provider_settings: dict) -> None: """Initialize the Responses API client. @@ -129,7 +138,11 @@ def _convert_chat_messages_to_response_input( and isinstance(state.get("items"), list) ): restored_items = [ - item + { + key: value + for key, value in item.items() + if key in self._REASONING_REPLAY_SAFE_KEYS + } for item in state["items"] if isinstance(item, dict) ] diff --git a/tests/test_openai_responses_source.py b/tests/test_openai_responses_source.py index 6b2d5e6718..2f510562dd 100644 --- a/tests/test_openai_responses_source.py +++ b/tests/test_openai_responses_source.py @@ -69,7 +69,6 @@ def test_convert_chat_history_preserves_response_items_and_function_calls(): reasoning_item = { "id": "rs_1", "type": "reasoning", - "status": "completed", "summary": [], "encrypted_content": "encrypted-reasoning", } @@ -148,6 +147,70 @@ def test_convert_chat_history_preserves_response_items_and_function_calls(): ] +def test_convert_chat_history_filters_nonportable_reasoning_replay_fields(): + provider = _make_provider() + reasoning_state = json.dumps( + { + "type": provider._REASONING_STATE_TYPE, + "items": [ + { + "id": "rs_1", + "type": "reasoning", + "status": "completed", + "summary": [], + "content": [ + { + "type": "reasoning_text", + "text": "prior reasoning", + } + ], + "encrypted_content": "encrypted-reasoning", + "provider_specific": "provider-only-value", + } + ], + } + ) + + response_input = provider._convert_chat_messages_to_response_input( + [ + { + "role": "assistant", + "content": [ + { + "type": "think", + "think": "prior reasoning", + "encrypted": reasoning_state, + }, + { + "type": "text", + "text": "prior answer", + }, + ], + } + ] + ) + + assert response_input == [ + { + "id": "rs_1", + "type": "reasoning", + "summary": [], + "content": [ + { + "type": "reasoning_text", + "text": "prior reasoning", + } + ], + "encrypted_content": "encrypted-reasoning", + }, + { + "type": "message", + "role": "assistant", + "content": "prior answer", + }, + ] + + def test_deepseek_converts_plain_reasoning_history_to_reasoning_item(): provider = _make_provider( {