From 28816ee95d42b13b897c2e71e28a42385e32d13d Mon Sep 17 00:00:00 2001 From: Imgyu Kim Date: Sat, 21 Mar 2026 19:15:46 +0900 Subject: [PATCH] fix: map 'refusal' finish_reason to 'content_filter' (#24141) Azure AI Foundry returns finish_reason='refusal' when content filtering rejects a response (e.g. Claude Sonnet 4.6 on Azure). This was unmapped and defaulted to 'stop', hiding the actual content filter rejection from callers. Add 'refusal' -> 'content_filter' to _FINISH_REASON_MAP. --- litellm/litellm_core_utils/core_helpers.py | 2 ++ tests/test_litellm/litellm_core_utils/test_core_helpers.py | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/litellm/litellm_core_utils/core_helpers.py b/litellm/litellm_core_utils/core_helpers.py index 256b16ff312..440adfe687c 100644 --- a/litellm/litellm_core_utils/core_helpers.py +++ b/litellm/litellm_core_utils/core_helpers.py @@ -98,6 +98,8 @@ _FINISH_REASON_MAP: dict[str, OpenAIChatCompletionFinishReason] = { "content_filter": "content_filter", # Anthropic Sonnet 4 "content_filtered": "content_filter", + # Azure AI Foundry content filtering + "refusal": "content_filter", } diff --git a/tests/test_litellm/litellm_core_utils/test_core_helpers.py b/tests/test_litellm/litellm_core_utils/test_core_helpers.py index 72c3b2b077e..f6def1135cd 100644 --- a/tests/test_litellm/litellm_core_utils/test_core_helpers.py +++ b/tests/test_litellm/litellm_core_utils/test_core_helpers.py @@ -123,6 +123,12 @@ class TestMapFinishReasonBedrock: assert map_finish_reason("guardrail_intervened") == "content_filter" +class TestMapFinishReasonAzureAIFoundry: + def test_refusal(self): + # GH#24141: Azure AI Foundry returns 'refusal' for content filtering + assert map_finish_reason("refusal") == "content_filter" + + class TestMapFinishReasonOpenAIPassthrough: @pytest.mark.parametrize( "reason", ["stop", "length", "tool_calls", "function_call", "content_filter"]