Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/heretic/scorers/keyword_rate.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (C) 2025-2026 Philipp Emanuel Weidmann <pew@worldwidemann.com> + contributors

from pydantic import BaseModel, Field
from pydantic import BaseModel, Field, field_validator
from rich.markup import escape

from heretic.config import DatasetSpecification, SingleDatasetSpecification
Expand Down Expand Up @@ -65,6 +65,14 @@ class Settings(BaseModel):
description="Strings whose presence in a response (case insensitive) identifies the response as a keyword match.",
)

@field_validator("keyword_markers")
@classmethod
def validate_keyword_markers(cls, value: list[str]) -> list[str]:
if any(not marker.strip() for marker in value):
raise ValueError("keyword markers cannot be empty or whitespace")

return value

print_responses: bool = Field(
default=False,
description="Whether to print prompt/response pairs when counting keyword matches.",
Expand Down
17 changes: 17 additions & 0 deletions tests/test_keyword_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import cast
from unittest.mock import patch

from pydantic import ValidationError
from rich.console import Console

from heretic.config import Settings as HereticSettings
Expand All @@ -23,6 +24,22 @@ def get_responses(self, prompts: list[Prompt]) -> list[str]:


class KeywordRateTests(unittest.TestCase):
def test_rejects_empty_keyword_markers(self) -> None:
for markers in [[""], [" ", "\t"]]:
with self.subTest(markers=markers):
with self.assertRaisesRegex(
ValidationError, "keyword markers cannot be empty or whitespace"
):
Settings(keyword_markers=markers)

def test_allows_empty_keyword_marker_list(self) -> None:
scorer = KeywordRate(
heretic_settings=HereticSettings.model_construct(),
settings=Settings(keyword_markers=[]),
)

self.assertFalse(scorer._is_match("This is a normal answer."))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But passing an empty keyword list doesn't make sense, and should also be disallowed.


def test_print_responses_shows_brackets_literally(self) -> None:
scorer = KeywordRate(
heretic_settings=HereticSettings.model_construct(),
Expand Down
Loading