From 2d192dc2454e33d4357b1687d1fb0f9b79e15fa7 Mon Sep 17 00:00:00 2001 From: Zhuo2021 <90879426+Zhuo2021@users.noreply.github.com> Date: Fri, 2 Oct 2026 19:25:14 +0800 Subject: [PATCH] fix(config): reject boolean title max_words/max_chars (#6187) YAML true was coercing to 1 and silently clamping auto titles to a single word. Co-authored-by: Cursor --- .../harness/deerflow/config/title_config.py | 11 ++++++++++- backend/tests/test_title_generation.py | 13 +++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/backend/packages/harness/deerflow/config/title_config.py b/backend/packages/harness/deerflow/config/title_config.py index 5aa750bc78..679183e6ce 100644 --- a/backend/packages/harness/deerflow/config/title_config.py +++ b/backend/packages/harness/deerflow/config/title_config.py @@ -1,6 +1,6 @@ """Configuration for automatic thread title generation.""" -from pydantic import BaseModel, Field +from pydantic import BaseModel, Field, ValidationInfo, field_validator class TitleConfig(BaseModel): @@ -31,6 +31,15 @@ class TitleConfig(BaseModel): description="Prompt template for LLM title generation when model_name is set", ) + @field_validator("max_words", "max_chars", mode="before") + @classmethod + def _reject_boolean_limits(cls, value: object, info: ValidationInfo) -> object: + # YAML `true`/`false` next to neighbouring boolean knobs must not coerce + # into integers (Pydantic would turn `true` into 1 before ge/le runs). + if isinstance(value, bool): + raise ValueError(f"{info.field_name} must be an integer, not a boolean") + return value + # Global configuration instance _title_config: TitleConfig = TitleConfig() diff --git a/backend/tests/test_title_generation.py b/backend/tests/test_title_generation.py index 53b0a50102..61ce71eab6 100644 --- a/backend/tests/test_title_generation.py +++ b/backend/tests/test_title_generation.py @@ -44,6 +44,19 @@ class TestTitleConfig: with pytest.raises(ValueError): TitleConfig(max_chars=201) + @pytest.mark.parametrize("field_name", ["max_words", "max_chars"]) + @pytest.mark.parametrize("bad_value", [True, False]) + def test_config_rejects_boolean_limits(self, field_name: str, bad_value: bool): + """YAML booleans must fail loudly instead of coercing into integers.""" + with pytest.raises(ValueError, match="must be an integer, not a boolean"): + TitleConfig(**{field_name: bad_value}) + + def test_config_still_accepts_numeric_strings(self): + """Numeric strings remain valid after the boolean guard.""" + config = TitleConfig(max_words="8", max_chars="80") + assert config.max_words == 8 + assert config.max_chars == 80 + def test_get_set_config(self): """Test global config getter and setter.""" original_config = get_title_config()