Description
filter_values_equal is documented as comparing "without equating booleans to numbers", and it does that for scalars, lists, tuples and mappings. It does not do it for sets, and require_filter_collection accepts sets and frozensets as filter values.
from agent_framework._vector_filters import filter_values_equal
filter_values_equal(True, 1) # False (correct)
filter_values_equal([True], [1]) # False (correct)
filter_values_equal({"a": True}, {"a": 1}) # False (correct)
filter_values_equal({True}, {1}) # True <-- a bool equated to a number
filter_values_equal(frozenset({True}), frozenset({1})) # True
filter_values_equal({(True,)}, {(1,)}) # True
{1} == {True} is true in Python because True hashes and compares equal to 1, and a set cannot be walked pairwise the way the sequence branch walks a list, so the existing code falls through to plain ==.
This is not just the helper: _evaluate_filter in _vectors.py uses filter_values_equal for eq, ne, in, not_in, contains, contains_any and contains_all. A record whose field is {1} therefore matches a filter looking for {True}, and vice versa — the exact confusion the function exists to prevent.
The parametrized table in test_filter_values_equal_preserves_nested_types is thorough for lists, tuples, dicts and scalars but contains no set or frozenset case, which is probably why this slipped through.
Reproduction
Any of the three calls above. Through the public path, an in-memory collection record with a set-valued field and an eq filter carrying the corresponding bool/int set will match when it should not.
Expected
Sets compare like the other containers: {True} is not {1}, while {1} still equals {1.0} (ints and floats stay interchangeable, as the scalar case already has it), and {1} still equals frozenset({1}) since Python compares those equal by contents.
Description
filter_values_equalis documented as comparing "without equating booleans to numbers", and it does that for scalars, lists, tuples and mappings. It does not do it for sets, andrequire_filter_collectionaccepts sets and frozensets as filter values.{1} == {True}is true in Python becauseTruehashes and compares equal to1, and a set cannot be walked pairwise the way the sequence branch walks a list, so the existing code falls through to plain==.This is not just the helper:
_evaluate_filterin_vectors.pyusesfilter_values_equalforeq,ne,in,not_in,contains,contains_anyandcontains_all. A record whose field is{1}therefore matches a filter looking for{True}, and vice versa — the exact confusion the function exists to prevent.The parametrized table in
test_filter_values_equal_preserves_nested_typesis thorough for lists, tuples, dicts and scalars but contains no set or frozenset case, which is probably why this slipped through.Reproduction
Any of the three calls above. Through the public path, an in-memory collection record with a set-valued field and an
eqfilter carrying the corresponding bool/int set will match when it should not.Expected
Sets compare like the other containers:
{True}is not{1}, while{1}still equals{1.0}(ints and floats stay interchangeable, as the scalar case already has it), and{1}still equalsfrozenset({1})since Python compares those equal by contents.