diff --git a/instructor/v2/dsl/citation.py b/instructor/v2/dsl/citation.py index c5ccd4f04..36b07dc0f 100644 --- a/instructor/v2/dsl/citation.py +++ b/instructor/v2/dsl/citation.py @@ -85,7 +85,7 @@ def _get_span( errs_ = 0 s = regex.search(f"({minor}){{e<={errs_}}}", major) - while s is None and errs_ <= errs: + while s is None and errs_ < errs: errs_ += 1 s = regex.search(f"({minor}){{e<={errs_}}}", major) diff --git a/tests/dsl/test_citation.py b/tests/dsl/test_citation.py new file mode 100644 index 000000000..53c3b2df4 --- /dev/null +++ b/tests/dsl/test_citation.py @@ -0,0 +1,26 @@ +"""Tests for CitationMixin fuzzy span matching tolerance.""" + +from instructor.v2.dsl.citation import CitationMixin + + +class _Answer(CitationMixin): + pass + + +def test_quote_within_error_tolerance_matches(): + # 3 substitutions from the 10-char source (<= errs default of 5): should match. + context = "0123456789" + result = _Answer.model_validate( + {"substring_quotes": ["0123456ZZZ"]}, context={"context": context} + ) + assert result.substring_quotes == ["0123456789"] + + +def test_quote_beyond_error_tolerance_is_dropped(): + # 6 substitutions from the 10-char source (> errs default of 5): should be + # dropped as "not found", not accepted and rewritten to an unrelated span. + context = "0123456789" + result = _Answer.model_validate( + {"substring_quotes": ["0123ZZZZZZ"]}, context={"context": context} + ) + assert result.substring_quotes == []