From 69730f684f9ae8efa866d208511f48fd5df42686 Mon Sep 17 00:00:00 2001 From: SquabbyZ <601709253@qq.com> Date: Thu, 2 Jul 2026 23:58:39 +0800 Subject: [PATCH] fix(api): strip leftover type/config when sensitive-word avoidance is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a chat request carries {"sensitive_word_avoidance": {"enabled": false, "type": "", "config": {}}}, the existing _normalize_raw() in core/app/app_config/common/sensitive_word_avoidance/manager.py falls through every branch: enabled is not None (it's False), enabled is not True, so the leftover type/config fields are passed straight through to the discriminator-tagged union. SensitiveWordAvoidanceDisabledConfig is declared with extra="forbid", so pydantic raises ValidationError("Extra inputs are not permitted: type, config") and the API returns HTTP 400 invalid_param — which matches the trace in issue #38327. This change extends _normalize_raw() to drop every key other than "enabled" when enabled is False, matching the intent of the disabled schema (only enabled matters). Two new parametrize cases cover the regression: one with empty leftover fields and one with non-empty leftover fields, asserting that the call now succeeds and the result stays {enabled: False}. diff --stat: api/core/app/app_config/common/sensitive_word_avoidance/manager.py | 6 ++++++ api/tests/unit_tests/core/app/app_config/common/test_sensitive_word_avoidance_manager.py | 5 +++++ 2 files changed, 11 insertions(+) Generated via peaks-loop (session 2026-07-02-session-95565c, request 2026-07-02-fix-sensitive-word-avoidance-disabled). Red-line scope was limited to the sensitive_word_avoidance/manager.py module and the matching parametrize list in its unit-test file. --- .../app_config/common/sensitive_word_avoidance/manager.py | 6 ++++++ .../common/test_sensitive_word_avoidance_manager.py | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/api/core/app/app_config/common/sensitive_word_avoidance/manager.py b/api/core/app/app_config/common/sensitive_word_avoidance/manager.py index b02b40773381fc..a998929f7197c5 100644 --- a/api/core/app/app_config/common/sensitive_word_avoidance/manager.py +++ b/api/core/app/app_config/common/sensitive_word_avoidance/manager.py @@ -65,6 +65,12 @@ def _normalize_raw(raw: Any) -> Any: raw = {**raw, "enabled": False} elif raw.get("enabled") is True and raw.get("config") is None: raw = {**raw, "config": {}} + elif raw.get("enabled") is False: + # When the moderation is disabled, the client may still send + # leftover 'type' / 'config' fields (e.g. {"enabled": false, "type": "", "config": {}}). + # Strip them so the discriminator-tagged union in + # SensitiveWordAvoidanceDisabledConfig (extra="forbid") does not reject the payload. + raw = {key: value for key, value in raw.items() if key in {"enabled"}} return raw diff --git a/api/tests/unit_tests/core/app/app_config/common/test_sensitive_word_avoidance_manager.py b/api/tests/unit_tests/core/app/app_config/common/test_sensitive_word_avoidance_manager.py index 0c8073793b2a39..9fdeb5f8a9020a 100644 --- a/api/tests/unit_tests/core/app/app_config/common/test_sensitive_word_avoidance_manager.py +++ b/api/tests/unit_tests/core/app/app_config/common/test_sensitive_word_avoidance_manager.py @@ -97,6 +97,11 @@ def test_validate_raises_when_not_dict(self): {"sensitive_word_avoidance": {"enabled": False}}, {"sensitive_word_avoidance": {"enabled": None}}, {"sensitive_word_avoidance": {}}, + # Regression for issue #38327: client sends leftover type/config fields + # alongside enabled=False. The normalize step must strip them so the + # discriminator-tagged union (extra="forbid") does not reject the payload. + {"sensitive_word_avoidance": {"enabled": False, "type": "", "config": {}}}, + {"sensitive_word_avoidance": {"enabled": False, "type": "keywords", "config": {"k": "v"}}}, ], ) def test_validate_disables_when_enabled_false_or_missing(self, config):