From 031f38d55957a97039ad569e6bb46c4a8aab803c Mon Sep 17 00:00:00 2001 From: Kevin Turcios Date: Sun, 18 Oct 2026 06:36:11 -0500 Subject: [PATCH] fix: give CompiledSearchTerm a None related_terms default CompiledSearchTerm was a stdlib dataclass subclassing the pydantic dataclass SearchTerm, so it took SearchTerm's pydantic Field object as the default of related_terms instead of None. CompiledSearchTerm(Term("x")) therefore had a truthy, non-list related_terms, which crashed dedupe_related_terms. Declare it with the same pydantic dataclass decorator as SearchTerm. Co-authored-by: wtfyd-agent[bot] <328989958+wtfyd-agent[bot]@users.noreply.github.com> --- src/typeagent/knowpro/query.py | 5 ++++- tests/test_query.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/typeagent/knowpro/query.py b/src/typeagent/knowpro/query.py index 1707cafc..005ef7a3 100644 --- a/src/typeagent/knowpro/query.py +++ b/src/typeagent/knowpro/query.py @@ -21,6 +21,7 @@ TextRangesInScope, ) from .common import is_search_term_wildcard +from .dataclasses import dataclass as pydantic_dataclass from .interfaces import ( DateRange, Datetime, @@ -54,7 +55,9 @@ # TODO: Move to compilelib.py -@dataclass +# A pydantic dataclass, like SearchTerm: a stdlib dataclass would take +# SearchTerm's pydantic Field object as the default of related_terms. +@pydantic_dataclass class CompiledSearchTerm(SearchTerm): related_terms_required: bool = False diff --git a/tests/test_query.py b/tests/test_query.py index 86b8cf11..261fcfea 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -24,6 +24,7 @@ Topic, ) from typeagent.knowpro.query import ( + CompiledSearchTerm, get_text_range_for_date_range, GetScopeExpr, is_conversation_searchable, @@ -42,6 +43,7 @@ ) from typeagent.storage.memory import MemorySemanticRefCollection from typeagent.storage.memory.property_index import PropertyIndex +from typeagent.storage.memory.related_terms_index import dedupe_related_terms from conftest import FakeConversation, FakeMessage, FakeTermIndex @@ -799,3 +801,16 @@ async def eval(self, context: QueryEvalContext) -> SemanticRefAccumulator: assert "term_a" in filtered.search_term_matches filtered.search_term_matches.add("new_term") assert "new_term" not in src.search_term_matches + + +def test_compiled_search_term_defaults() -> None: + term = CompiledSearchTerm(Term("python")) + assert term.related_terms is None + assert term.related_terms_required is False + + +def test_dedupe_related_terms_accepts_default_compiled_terms() -> None: + # A default related_terms that isn't None used to crash TermSet.add_or_union. + terms = [CompiledSearchTerm(Term("python")), CompiledSearchTerm(Term("java"))] + dedupe_related_terms(terms, ensure_single_occurrence=True) + assert [t.related_terms for t in terms] == [None, None]