Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
13 changes: 12 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,17 @@ 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 not value:
raise ValueError("keyword marker list cannot be empty")

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
15 changes: 15 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,20 @@ 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_rejects_empty_keyword_marker_list(self) -> None:
with self.assertRaisesRegex(
ValidationError, "keyword marker list cannot be empty"
):
Settings(keyword_markers=[])

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