1414
1515from __future__ import annotations
1616
17+ import re
1718from typing import Optional
1819
1920from google .genai import types as genai_types
@@ -96,6 +97,31 @@ def _get_eval_status(score: float, threshold: float) -> EvalStatus:
9697 return EvalStatus .PASSED if score >= threshold else EvalStatus .FAILED
9798
9899
100+ def _contains_non_ascii (text : str ) -> bool :
101+ """Returns True if the text contains any non-ASCII characters."""
102+ return bool (re .search (r"[^\x00-\x7F]" , text ))
103+
104+
105+ class _UnicodeTokenizer :
106+ """A tokenizer that handles non-ASCII text by splitting on whitespace and
107+ decomposing non-ASCII tokens into individual characters."""
108+
109+ def tokenize (self , text : str ) -> list [str ]:
110+ tokens = []
111+ for word in text .lower ().split ():
112+ if _contains_non_ascii (word ):
113+ # For non-ASCII words (e.g. Thai, Chinese, Arabic), treat each
114+ # Unicode character as a separate token so that ROUGE overlap is
115+ # computed at the character level instead of being discarded entirely.
116+ tokens .extend (list (word ))
117+ else :
118+ # Keep ASCII tokens as-is (drop purely punctuation-only tokens).
119+ cleaned = re .sub (r"[^a-z0-9]" , "" , word )
120+ if cleaned :
121+ tokens .append (cleaned )
122+ return tokens
123+
124+
99125def _calculate_rouge_1_scores (candidate : str , reference : str ):
100126 """Calculates the ROUGE-1 score between a candidate and reference text.
101127
@@ -114,7 +140,14 @@ def _calculate_rouge_1_scores(candidate: str, reference: str):
114140 Returns:
115141 A dictionary containing the ROUGE-1 precision, recall, and f-measure.
116142 """
117- scorer = rouge_scorer .RougeScorer (["rouge1" ], use_stemmer = True )
143+ # Use a Unicode-aware tokenizer when either text contains non-ASCII
144+ # characters (e.g. Thai, Chinese, Arabic) so that the default ASCII-only
145+ # tokenizer does not strip every token and return a spurious score of 0.
146+ if _contains_non_ascii (candidate ) or _contains_non_ascii (reference ):
147+ tokenizer = _UnicodeTokenizer ()
148+ scorer = rouge_scorer .RougeScorer (["rouge1" ], tokenizer = tokenizer )
149+ else :
150+ scorer = rouge_scorer .RougeScorer (["rouge1" ], use_stemmer = True )
118151
119152 # The score method returns a dictionary where keys are the ROUGE types
120153 # and values are Score objects (tuples) with precision, recall, and fmeasure.
0 commit comments