flexeval 0.17.3__tar.gz → 0.18.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. {flexeval-0.17.3 → flexeval-0.18.1}/PKG-INFO +8 -6
  2. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/__init__.py +1 -0
  3. flexeval-0.18.1/flexeval/core/chat_dataset/image_preprocessors.py +141 -0
  4. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/template_based.py +15 -4
  5. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/language_model/vllm_serve_lm.py +32 -4
  6. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/llm_label.py +18 -0
  7. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/llm_score.py +19 -1
  8. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reward_model/pairwise_judge_reward_model.py +30 -14
  9. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/__init__.py +1 -0
  10. flexeval-0.18.1/flexeval/core/string_processor/last_choice.py +81 -0
  11. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/utils/jinja2_utils.py +13 -0
  12. flexeval-0.18.1/flexeval/preset_configs/EvalSetup/en_vision/mmmu.jsonnet +100 -0
  13. flexeval-0.18.1/flexeval/preset_configs/EvalSetup/ja_chat/jamcqa_chat.jsonnet +56 -0
  14. flexeval-0.18.1/flexeval/preset_configs/EvalSetup/ja_vision/heron_bench.jsonnet +66 -0
  15. flexeval-0.18.1/flexeval/preset_configs/EvalSetup/ja_vision/jmmmu.jsonnet +102 -0
  16. flexeval-0.18.1/flexeval/preset_configs/Metric/heron_bench_judge.jsonnet +52 -0
  17. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/utils/hf_utils.py +14 -10
  18. {flexeval-0.17.3 → flexeval-0.18.1}/pyproject.toml +9 -7
  19. {flexeval-0.17.3 → flexeval-0.18.1}/LICENSE +0 -0
  20. {flexeval-0.17.3 → flexeval-0.18.1}/README.md +0 -0
  21. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/__init__.py +0 -0
  22. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/__init__.py +0 -0
  23. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/base.py +0 -0
  24. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench.py +0 -0
  25. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/README.md +0 -0
  26. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/mt-en-ref-gpt4.jsonl +0 -0
  27. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/mt-en.jsonl +0 -0
  28. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/mt-ja-ref-gpt4.jsonl +0 -0
  29. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/mt-ja-ref-gpt4o-with-human-annotation.jsonl +0 -0
  30. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/mt-ja.jsonl +0 -0
  31. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/rakuda-v2-ja.jsonl +0 -0
  32. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/vicuna-en-ref-gpt4.jsonl +0 -0
  33. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/vicuna-en.jsonl +0 -0
  34. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/vicuna-ja-ref-gpt4.jsonl +0 -0
  35. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/chatbot_bench_datasets/vicuna-ja.jsonl +0 -0
  36. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/openai_messages.py +0 -0
  37. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/chat_dataset/sacrebleu_dataset.py +0 -0
  38. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/eval_setups.py +0 -0
  39. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/evaluate_chat_response.py +0 -0
  40. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/evaluate_from_data.py +0 -0
  41. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/evaluate_generation.py +0 -0
  42. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/evaluate_multiple_choice.py +0 -0
  43. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/evaluate_pairwise.py +0 -0
  44. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/evaluate_perplexity.py +0 -0
  45. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/evaluate_reward_model.py +0 -0
  46. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/few_shot_generator/__init__.py +0 -0
  47. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/few_shot_generator/balanced.py +0 -0
  48. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/few_shot_generator/base.py +0 -0
  49. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/few_shot_generator/fixed.py +0 -0
  50. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/few_shot_generator/rand.py +0 -0
  51. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/generation_dataset/__init__.py +0 -0
  52. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/generation_dataset/base.py +0 -0
  53. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/generation_dataset/sacrebleu_dataset.py +0 -0
  54. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/generation_dataset/template_based.py +0 -0
  55. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/language_model/__init__.py +0 -0
  56. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/language_model/base.py +0 -0
  57. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/language_model/hf_lm.py +0 -0
  58. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/language_model/litellm_api.py +0 -0
  59. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/language_model/openai_api.py +0 -0
  60. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/language_model/openai_batch_api.py +0 -0
  61. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/language_model/vllm_model.py +0 -0
  62. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/__init__.py +0 -0
  63. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/base.py +0 -0
  64. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/bleu.py +0 -0
  65. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/char_f1.py +0 -0
  66. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/code_eval.py +0 -0
  67. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/common_prefix_length.py +0 -0
  68. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/common_string_length.py +0 -0
  69. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/correlation.py +0 -0
  70. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/exact_match.py +0 -0
  71. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/finish_reason.py +0 -0
  72. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/llm_geval_score.py +0 -0
  73. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/math.py +0 -0
  74. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/output_length_stats.py +0 -0
  75. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/perspective_api.py +0 -0
  76. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/repetition_count.py +0 -0
  77. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/repetition_n.py +0 -0
  78. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/rouge.py +0 -0
  79. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/sari.py +0 -0
  80. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/substring_match.py +0 -0
  81. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/tool_call.py +0 -0
  82. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/utils.py +0 -0
  83. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/metric/xer.py +0 -0
  84. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/multiple_choice_dataset/__init__.py +0 -0
  85. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/multiple_choice_dataset/base.py +0 -0
  86. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/multiple_choice_dataset/template_based.py +0 -0
  87. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/__init__.py +0 -0
  88. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/judge/__init__.py +0 -0
  89. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/judge/base.py +0 -0
  90. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/judge/llm_judge.py +0 -0
  91. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/match.py +0 -0
  92. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/match_maker/__init__.py +0 -0
  93. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/match_maker/all_combinations.py +0 -0
  94. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/match_maker/base.py +0 -0
  95. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/match_maker/random_combinations.py +0 -0
  96. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/scorer/__init__.py +0 -0
  97. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/scorer/base.py +0 -0
  98. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/scorer/bradley_terry.py +0 -0
  99. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/pairwise_comparison/scorer/win_rate.py +0 -0
  100. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/prompt_template/__init__.py +0 -0
  101. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/prompt_template/base.py +0 -0
  102. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/prompt_template/jinja2.py +0 -0
  103. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reasoning_parser/__init__.py +0 -0
  104. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reasoning_parser/base.py +0 -0
  105. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reasoning_parser/regex_reasoning_parser.py +0 -0
  106. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/result_recorder/__init__.py +0 -0
  107. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/result_recorder/base.py +0 -0
  108. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/result_recorder/local_recorder.py +0 -0
  109. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/result_recorder/wandb_recorder.py +0 -0
  110. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reward_bench_dataset/__init__.py +0 -0
  111. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reward_bench_dataset/base.py +0 -0
  112. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reward_bench_dataset/template_based.py +0 -0
  113. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reward_model/__init__.py +0 -0
  114. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reward_model/base.py +0 -0
  115. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reward_model/log_prob.py +0 -0
  116. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/reward_model/sequence_classification.py +0 -0
  117. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/aio.py +0 -0
  118. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/base.py +0 -0
  119. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/last_line.py +0 -0
  120. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/lower.py +0 -0
  121. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/mgsm.py +0 -0
  122. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/nfkc.py +0 -0
  123. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/regex.py +0 -0
  124. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/string_strip.py +0 -0
  125. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/string_processor/template.py +0 -0
  126. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/text_dataset/__init__.py +0 -0
  127. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/text_dataset/base.py +0 -0
  128. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/text_dataset/hf.py +0 -0
  129. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/text_dataset/jsonl.py +0 -0
  130. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tokenizer/__init__.py +0 -0
  131. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tokenizer/base.py +0 -0
  132. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tokenizer/mecab.py +0 -0
  133. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tokenizer/sacrebleu_tokenizer.py +0 -0
  134. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tokenizer/tiktoken_tokenizer.py +0 -0
  135. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tokenizer/transformers_tokenizer.py +0 -0
  136. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tokenizer/whitespace.py +0 -0
  137. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tool_parser/__init__.py +0 -0
  138. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/tool_parser/base.py +0 -0
  139. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/utils/__init__.py +0 -0
  140. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/utils/chat_util.py +0 -0
  141. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/utils/data_util.py +0 -0
  142. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/core/utils/json_util.py +0 -0
  143. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/code_chat/mbpp_chat.jsonnet +0 -0
  144. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/code_generation/jhumaneval.jsonnet +0 -0
  145. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/code_generation/jhumaneval_tab_indent.jsonnet +0 -0
  146. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/code_generation/mbpp.jsonnet +0 -0
  147. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/code_generation/mbpp_tab_indent.jsonnet +0 -0
  148. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/code_generation/openai_humaneval.jsonnet +0 -0
  149. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/code_generation/openai_humaneval_tab_indent.jsonnet +0 -0
  150. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_chat/mt-en.jsonnet +0 -0
  151. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_chat/vicuna-en.jsonnet +0 -0
  152. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_generation/babi.jsonnet +0 -0
  153. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_generation/commonsense_qa.jsonnet +0 -0
  154. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_generation/gsm8k.jsonnet +0 -0
  155. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_generation/squad_v1.jsonnet +0 -0
  156. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_generation/trivia_qa.jsonnet +0 -0
  157. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_generation/twitter_sentiment.jsonnet +0 -0
  158. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_multiple_choice/arc_challenge.jsonnet +0 -0
  159. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_multiple_choice/arc_easy.jsonnet +0 -0
  160. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_multiple_choice/commonsense_qa_mc.jsonnet +0 -0
  161. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_multiple_choice/hellaswag.jsonnet +0 -0
  162. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_multiple_choice/openbookqa.jsonnet +0 -0
  163. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_multiple_choice/piqa.jsonnet +0 -0
  164. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_multiple_choice/xwinograd_en.jsonnet +0 -0
  165. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/en_perplexity/tiny_shakespeare.jsonnet +0 -0
  166. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_chat/aio_chat.jsonnet +0 -0
  167. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_chat/elyza_tasks_100.jsonnet +0 -0
  168. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_chat/mgsm_ja_chat.jsonnet +0 -0
  169. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_chat/mt-ja.jsonnet +0 -0
  170. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_chat/rakuda-v2-ja.jsonnet +0 -0
  171. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_chat/vicuna-ja.jsonnet +0 -0
  172. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_generation/aio.jsonnet +0 -0
  173. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_generation/jamcqa.jsonnet +0 -0
  174. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_generation/jcommonsenseqa.jsonnet +0 -0
  175. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_generation/jnli.jsonnet +0 -0
  176. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_generation/jsquad.jsonnet +0 -0
  177. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_generation/mgsm_ja.jsonnet +0 -0
  178. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_generation/wrime_pos_neg.jsonnet +0 -0
  179. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_generation/xlsum_ja.jsonnet +0 -0
  180. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_multiple_choice/jcommonsenseqa_mc.jsonnet +0 -0
  181. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/ja_multiple_choice/xwinograd_ja.jsonnet +0 -0
  182. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/translation/wmt20_en_ja.jsonnet +0 -0
  183. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/translation/wmt20_ja_en.jsonnet +0 -0
  184. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/translation_chat/wmt20_en_ja_chat.jsonnet +0 -0
  185. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/EvalSetup/translation_chat/wmt20_ja_en_chat.jsonnet +0 -0
  186. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/Metric/assistant_eval_en_single_turn.jsonnet +0 -0
  187. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/Metric/assistant_eval_ja_single_turn.jsonnet +0 -0
  188. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/Metric/elyza_tasks_100_eval.jsonnet +0 -0
  189. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/PairwiseJudge/assistant_judge_en_single_turn.jsonnet +0 -0
  190. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/preset_configs/PairwiseJudge/assistant_judge_ja_single_turn.jsonnet +0 -0
  191. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/scripts/__init__.py +0 -0
  192. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/scripts/common.py +0 -0
  193. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/scripts/flexeval_file.py +0 -0
  194. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/scripts/flexeval_lm.py +0 -0
  195. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/scripts/flexeval_pairwise.py +0 -0
  196. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/scripts/flexeval_presets.py +0 -0
  197. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/scripts/flexeval_reward.py +0 -0
  198. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/utils/__init__.py +0 -0
  199. {flexeval-0.17.3 → flexeval-0.18.1}/flexeval/utils/module_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flexeval
3
- Version: 0.17.3
3
+ Version: 0.18.1
4
4
  Summary:
5
5
  License-File: LICENSE
6
6
  Author: ryokan-ri
@@ -13,7 +13,7 @@ Classifier: Programming Language :: Python :: 3.12
13
13
  Provides-Extra: vllm
14
14
  Provides-Extra: wandb
15
15
  Requires-Dist: datasets (>=2.14.6,<3.0.0)
16
- Requires-Dist: evaluate (>=0.4.1,<0.5.0)
16
+ Requires-Dist: evaluate (>=0.4.6,<0.5.0)
17
17
  Requires-Dist: fuzzywuzzy (>=0.18.0,<0.19.0)
18
18
  Requires-Dist: google-api-python-client (>=2.131.0,<3.0.0)
19
19
  Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
@@ -22,8 +22,9 @@ Requires-Dist: jsonargparse[jsonnet] (>=4.26.1,<5.0.0)
22
22
  Requires-Dist: litellm (>=1.52.9,<2.0.0)
23
23
  Requires-Dist: loguru (>=0.7.2,<0.8.0)
24
24
  Requires-Dist: math-verify[antlr4-13-2] (>=0.7.0,<0.8.0)
25
- Requires-Dist: openai (>=1.52.2,<2.0.0)
26
- Requires-Dist: peft (>=0.10.0,<0.11.0)
25
+ Requires-Dist: openai (>=2.0.0,<3.0.0)
26
+ Requires-Dist: peft (>=0.19.1,<0.20.0)
27
+ Requires-Dist: pillow (>=11.0.0,<12.0.0)
27
28
  Requires-Dist: pyarrow (==16.1.0)
28
29
  Requires-Dist: python-levenshtein (>=0.23.0,<0.24.0)
29
30
  Requires-Dist: rouge (>=1.0.1,<2.0.0)
@@ -33,8 +34,9 @@ Requires-Dist: scipy (>=1.13.0,<2.0.0)
33
34
  Requires-Dist: smart-open (>=7.1.0,<8.0.0)
34
35
  Requires-Dist: sudachipy (>=0.6.10)
35
36
  Requires-Dist: tiktoken (>=0.9.0,<0.10.0)
36
- Requires-Dist: transformers[ja,sentencepiece,torch] (>=4.34.1,<5.0.0)
37
- Requires-Dist: vllm (==0.17.1) ; extra == "vllm"
37
+ Requires-Dist: transformers[ja,sentencepiece,torch] (>=5.5.3,<6)
38
+ Requires-Dist: uv (>=0.12.14,<0.13.0) ; extra == "vllm"
39
+ Requires-Dist: vllm (==0.24.0) ; extra == "vllm"
38
40
  Requires-Dist: wandb (>=0.17.2,<0.18.0) ; extra == "wandb"
39
41
  Description-Content-Type: text/markdown
40
42
 
@@ -1,5 +1,6 @@
1
1
  from .base import ChatDataset, ChatInstance
2
2
  from .chatbot_bench import ChatbotBench
3
+ from .image_preprocessors import ConvertImageListToBase64, ConvertImageToBase64, EnsureMinSize
3
4
  from .openai_messages import OpenAIMessagesDataset
4
5
  from .sacrebleu_dataset import SacreBleuChatDataset
5
6
  from .template_based import HFChatDataset, JsonlChatDataset, Preprocessor, TemplateChatDataset, load_jinja2_template
@@ -0,0 +1,141 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import dataclasses
5
+ from io import BytesIO
6
+ from typing import Any, Literal
7
+
8
+ from loguru import logger
9
+ from PIL import Image
10
+
11
+ from .template_based import Preprocessor
12
+
13
+ Data = dict[str, Any]
14
+
15
+
16
+ def image_to_base64(image: Image.Image, image_format: str, max_length: int | None = None) -> str:
17
+ """Encode a PIL image as a `data:image/...;base64,...` URL string.
18
+
19
+ If `max_length` is given and the encoded string exceeds it, the image is
20
+ iteratively downscaled until the encoded string fits.
21
+ """
22
+
23
+ def _to_base64(img: Image.Image) -> str:
24
+ buffer = BytesIO()
25
+ img.convert("RGB").save(buffer, format=image_format)
26
+ return base64.b64encode(buffer.getvalue()).decode("utf-8")
27
+
28
+ base64_image = _to_base64(image)
29
+ if max_length is not None and len(base64_image) > max_length:
30
+ factor = 1.0
31
+ new_size = image.size
32
+ while len(base64_image) > max_length:
33
+ factor *= 0.9
34
+ new_size = (int(image.width * factor), int(image.height * factor))
35
+ resized_image = image.resize(new_size, resample=Image.Resampling.BILINEAR)
36
+ base64_image = _to_base64(resized_image)
37
+ logger.debug(f"Image size reduced to {new_size} to fit max_length {max_length}.")
38
+ return f"data:image/{image_format.lower()};base64,{base64_image}"
39
+
40
+
41
+ def load_image(image: Image.Image | dict) -> Image.Image:
42
+ """Normalize an image value into a PIL Image.
43
+
44
+ Accepts PIL Image objects or dicts of the form `{"bytes": ..., "path": ...}`
45
+ (the raw form stored in parquet files that is obtained when images are
46
+ loaded without the HF `Image` feature metadata, e.g. via the `parquet`
47
+ dataset loader).
48
+ """
49
+ if isinstance(image, Image.Image):
50
+ return image
51
+ if isinstance(image, dict):
52
+ if image.get("bytes") is not None:
53
+ return Image.open(BytesIO(image["bytes"]))
54
+ if image.get("path") is not None:
55
+ return Image.open(image["path"])
56
+ msg = f"Unsupported image value: {type(image)}"
57
+ raise TypeError(msg)
58
+
59
+
60
+ @dataclasses.dataclass
61
+ class ConvertImageToBase64(Preprocessor):
62
+ """Convert an image stored under `key` to a base64 data-URL string stored
63
+ under `{key}_base64`, ready to be embedded as an OpenAI-compatible
64
+ `image_url` content part.
65
+ """
66
+
67
+ key: str
68
+ format: Literal["PNG", "JPEG"] = "PNG"
69
+ max_length: int | None = None
70
+
71
+ def __call__(self, item: Data) -> Data:
72
+ image = item[self.key]
73
+ if image is None:
74
+ base64_image = None
75
+ else:
76
+ base64_image = image_to_base64(
77
+ load_image(image),
78
+ image_format=self.format,
79
+ max_length=self.max_length,
80
+ )
81
+
82
+ item[f"{self.key}_base64"] = base64_image
83
+ return item
84
+
85
+
86
+ @dataclasses.dataclass
87
+ class ConvertImageListToBase64(Preprocessor):
88
+ """Convert a list of images (e.g., for multi-image VQA) stored under `key`
89
+ to a list of base64 data-URL strings stored under `{key}_base64`.
90
+ """
91
+
92
+ key: str
93
+ format: Literal["PNG", "JPEG"] = "PNG"
94
+ max_length: int | None = None
95
+
96
+ def __call__(self, item: Data) -> Data:
97
+ images = item[self.key]
98
+ if images is None:
99
+ item[f"{self.key}_base64"] = None
100
+ elif isinstance(images, (list, tuple)):
101
+ item[f"{self.key}_base64"] = [
102
+ image_to_base64(load_image(image), image_format=self.format, max_length=self.max_length)
103
+ for image in images
104
+ ]
105
+ else:
106
+ msg = f"Unsupported image list type: {type(images)}"
107
+ raise TypeError(msg)
108
+
109
+ return item
110
+
111
+
112
+ @dataclasses.dataclass
113
+ class EnsureMinSize(Preprocessor):
114
+ """Ensure the image under `key` is at least `min_size` pixels on its
115
+ shorter side, upscaling in place (preserving aspect ratio) if necessary.
116
+ """
117
+
118
+ key: str
119
+ min_size: int
120
+ interpolation: Image.Resampling = Image.Resampling.BILINEAR
121
+
122
+ def __call__(self, item: Data) -> Data:
123
+ image = item[self.key]
124
+ if image is None:
125
+ return item
126
+
127
+ if not isinstance(image, Image.Image):
128
+ msg = f"Unsupported image type: {type(image)}"
129
+ raise TypeError(msg)
130
+
131
+ width, height = image.size
132
+ if width >= self.min_size and height >= self.min_size:
133
+ return item
134
+ if width <= height:
135
+ new_width = self.min_size
136
+ new_height = int(self.min_size * height / width)
137
+ else:
138
+ new_height = self.min_size
139
+ new_width = int(self.min_size * width / height)
140
+ item[self.key] = image.resize((new_width, new_height), resample=self.interpolation)
141
+ return item
@@ -170,7 +170,9 @@ class HFChatDataset(TemplateChatDataset):
170
170
  path: The path to the Hugging Face dataset.
171
171
  split: The split of the dataset.
172
172
  input_template: A Jinja2 template for the user input.
173
- subset: The subset of the dataset.
173
+ subset: The subset of the dataset. If a list is given, all subsets are
174
+ loaded and concatenated, and each item gains a "subset" key with
175
+ its subset name (unless the column already exists).
174
176
  dataset_kwargs: The keyword arguments to pass to the Hugging Face dataset.
175
177
  """
176
178
 
@@ -179,7 +181,7 @@ class HFChatDataset(TemplateChatDataset):
179
181
  path: str,
180
182
  split: str,
181
183
  input_template: str | PathLike[str],
182
- subset: str | None = None,
184
+ subset: str | list[str] | None = None,
183
185
  dataset_kwargs: dict[str, Any] | None = None,
184
186
  reference_template: str | PathLike[str] | None = None,
185
187
  reference_list_template: str | PathLike[str] | None = None,
@@ -193,8 +195,17 @@ class HFChatDataset(TemplateChatDataset):
193
195
  preprocessors: list[Preprocessor] | None = None,
194
196
  ) -> None:
195
197
  dataset_kwargs = dataset_kwargs or {}
196
- dataset = datasets.load_dataset(path, name=subset, split=split, **dataset_kwargs)
197
- items = [dict(item) for item in dataset]
198
+ if isinstance(subset, list):
199
+ items = []
200
+ for subset_name in subset:
201
+ dataset = datasets.load_dataset(path, name=subset_name, split=split, **dataset_kwargs)
202
+ for item in dataset:
203
+ item_dict = dict(item)
204
+ item_dict.setdefault("subset", subset_name)
205
+ items.append(item_dict)
206
+ else:
207
+ dataset = datasets.load_dataset(path, name=subset, split=split, **dataset_kwargs)
208
+ items = [dict(item) for item in dataset]
198
209
 
199
210
  super().__init__(
200
211
  items=items,
@@ -39,15 +39,25 @@ class VLLMServerManager:
39
39
  model_kwargs: Additional keyword arguments to pass as command-line options to `vllm serve`.
40
40
  Each key-value pair is converted to a corresponding CLI argument.
41
41
  timeout: Maximum time in seconds to wait for the server to become available.
42
+ vllm_version: If specified, `vllm serve` is launched via `uvx --from vllm==<vllm_version>` instead of
43
+ the `vllm` command on the current environment. This allows running any vllm version regardless of
44
+ what (if anything) is installed in the current Python environment.
42
45
  """
43
46
 
44
- def __init__(self, model: str, model_kwargs: dict[str, Any] | None = None, timeout: int = 3600) -> None:
47
+ def __init__(
48
+ self,
49
+ model: str,
50
+ model_kwargs: dict[str, Any] | None = None,
51
+ timeout: int = 3600,
52
+ vllm_version: str | None = None,
53
+ ) -> None:
45
54
  self.model = model
46
55
  self.model_kwargs = model_kwargs or {}
47
56
  self.host = "localhost"
48
57
  self.port = find_free_port()
49
58
  self.base_url = f"http://{self.host}:{self.port}/v1"
50
59
  self.timeout = timeout
60
+ self.vllm_version = vllm_version
51
61
  self.process: subprocess.Popen | None = None
52
62
  self._log_threads: list[threading.Thread] = []
53
63
 
@@ -59,8 +69,8 @@ class VLLMServerManager:
59
69
  logger.warning("vLLM server is already running. Skipping start.")
60
70
  return self.host, self.port
61
71
 
62
- cmd = [
63
- "vllm",
72
+ cmd = ["uvx", "--from", f"vllm=={self.vllm_version}", "vllm"] if self.vllm_version is not None else ["vllm"]
73
+ cmd += [
64
74
  "serve",
65
75
  self.model,
66
76
  "--host",
@@ -106,6 +116,17 @@ class VLLMServerManager:
106
116
 
107
117
  interval_second = 1
108
118
  for _ in range(self.timeout // interval_second):
119
+ # Fail fast if the vLLM process disappears or exits while the server is starting.
120
+ if self.process is None:
121
+ msg = "vLLM server process is not initialized."
122
+ raise RuntimeError(msg)
123
+
124
+ returncode = self.process.poll()
125
+ if returncode is not None:
126
+ self.stop()
127
+ msg = f"vLLM server process terminated unexpectedly. {returncode=}"
128
+ raise RuntimeError(msg)
129
+
109
130
  if self.is_ready():
110
131
  logger.info(f"vLLM server is ready at {self.base_url}")
111
132
  return self.host, self.port
@@ -164,6 +185,10 @@ class VLLMServeLM(OpenAIChatAPI):
164
185
  Do not include the prefix "--".
165
186
  See also: https://docs.vllm.ai/en/latest/cli/index.html#options
166
187
  booting_timeout: Maximum time in seconds to wait for the server to become available.
188
+ vllm_version: If specified, `vllm serve` is launched via `uvx --from vllm==<vllm_version>` instead of
189
+ the `vllm` command on the current environment, so that any vllm version can be used regardless
190
+ of the version (if any) installed in the current Python environment. Requires `uv`/`uvx` to be
191
+ available on PATH.
167
192
  default_gen_kwargs: Default generation kwargs to use when calling the API.
168
193
  developer_message: Instructions to the model that are prioritized ahead of user messages.
169
194
  Previously called the system prompt.
@@ -180,6 +205,7 @@ class VLLMServeLM(OpenAIChatAPI):
180
205
  api_headers: dict[str, str] | None = None,
181
206
  model_kwargs: dict[str, Any] | None = None,
182
207
  booting_timeout: int = 3600,
208
+ vllm_version: str | None = None,
183
209
  default_gen_kwargs: dict[str, Any] | None = None,
184
210
  developer_message: str | None = None,
185
211
  string_processors: StringProcessor | list[StringProcessor] | None = None,
@@ -195,7 +221,9 @@ class VLLMServeLM(OpenAIChatAPI):
195
221
  model_kwargs = model_kwargs or {}
196
222
  if "tensor_parallel_size" not in model_kwargs and "tensor-parallel-size" not in model_kwargs:
197
223
  model_kwargs["tensor_parallel_size"] = torch.cuda.device_count()
198
- self.manager = VLLMServerManager(model=model, model_kwargs=model_kwargs, timeout=booting_timeout)
224
+ self.manager = VLLMServerManager(
225
+ model=model, model_kwargs=model_kwargs, timeout=booting_timeout, vllm_version=vllm_version
226
+ )
199
227
  if api_headers is None:
200
228
  api_headers = {}
201
229
  api_headers["base_url"] = self.manager.base_url
@@ -104,6 +104,8 @@ class LLMLabel(Metric):
104
104
  category_key: A key to create category-wise mean score.
105
105
  The category key is expected to be in extra_info.
106
106
  metric_prefix: A prefix to be added to the metric keys in the summary and instance details.
107
+ output_reasoning_text: If True, store the evaluator's reasoning content
108
+ as `llm_label_reasoning_text` in the instance details.
107
109
 
108
110
  Examples:
109
111
  >>> from flexeval import OpenAIChatAPI, Jinja2PromptTemplate, LLMLabel
@@ -146,6 +148,7 @@ class LLMLabel(Metric):
146
148
  valid_score_range: tuple[int, int] | None = None,
147
149
  category_key: str | None = None,
148
150
  metric_prefix: str | None = None,
151
+ output_reasoning_text: bool = False,
149
152
  ) -> None:
150
153
  self.language_model = language_model
151
154
  self.prompt_template = prompt_template
@@ -166,6 +169,7 @@ class LLMLabel(Metric):
166
169
  self.valid_score_range = valid_score_range
167
170
  self.category_key = category_key
168
171
  self.metric_prefix = f"{metric_prefix}-" if metric_prefix else ""
172
+ self.output_reasoning_text = output_reasoning_text
169
173
 
170
174
  def evaluate(
171
175
  self,
@@ -218,6 +222,11 @@ class LLMLabel(Metric):
218
222
  f"{self.metric_prefix}llm_label_input": eval_in,
219
223
  f"{self.metric_prefix}llm_label_output": eval_out.text,
220
224
  }
225
+ | (
226
+ {f"{self.metric_prefix}llm_label_reasoning_text": eval_out.reasoning_text}
227
+ if self.output_reasoning_text
228
+ else {}
229
+ )
221
230
  for eval_label, eval_score, eval_in, eval_out in zip(
222
231
  evaluator_label_list,
223
232
  evaluator_score_list,
@@ -251,6 +260,8 @@ class ChatLLMLabel(Metric):
251
260
  category_key: A key to create category-wise mean score.
252
261
  The category key is expected to be in extra_info.
253
262
  metric_prefix: A prefix to be added to the metric keys in the summary and instance details.
263
+ output_reasoning_text: If True, store the evaluator's reasoning content
264
+ as `llm_label_reasoning_text` in the instance details.
254
265
 
255
266
  Examples:
256
267
  >>> from flexeval import ChatLLMScore, OpenAIChatAPI, Jinja2PromptTemplate
@@ -294,6 +305,7 @@ class ChatLLMLabel(Metric):
294
305
  disable_tqdm: bool = False,
295
306
  category_key: str | None = None,
296
307
  metric_prefix: str | None = None,
308
+ output_reasoning_text: bool = False,
297
309
  ) -> None:
298
310
  self.language_model = language_model
299
311
  self.prompt_template = prompt_template
@@ -314,6 +326,7 @@ class ChatLLMLabel(Metric):
314
326
  self.disable_tqdm = disable_tqdm
315
327
  self.category_key = category_key
316
328
  self.metric_prefix = f"{metric_prefix}-" if metric_prefix else ""
329
+ self.output_reasoning_text = output_reasoning_text
317
330
 
318
331
  def evaluate(
319
332
  self,
@@ -367,6 +380,11 @@ class ChatLLMLabel(Metric):
367
380
  f"{self.metric_prefix}llm_label_input": eval_in,
368
381
  f"{self.metric_prefix}llm_label_output": eval_out.text,
369
382
  }
383
+ | (
384
+ {f"{self.metric_prefix}llm_label_reasoning_text": eval_out.reasoning_text}
385
+ if self.output_reasoning_text
386
+ else {}
387
+ )
370
388
  for eval_label, eval_score, eval_in, eval_out in zip(
371
389
  evaluator_label_list,
372
390
  evaluator_score_list,
@@ -161,7 +161,7 @@ def generate_evaluations(
161
161
  disable=disable_tqdm,
162
162
  desc=desc_for_tqdm,
163
163
  ) as pbar:
164
- evaluator_output_list: list[str] = []
164
+ evaluator_output_list: list[LMOutput] = []
165
165
  for batch_inputs in batch_iter(
166
166
  evaluator_input_list,
167
167
  batch_size=batch_size,
@@ -196,6 +196,8 @@ class LLMScore(Metric):
196
196
  The category keys are expected to be in extra_info.
197
197
  metric_prefix: A prefix to be added to the metric keys in the summary and instance details.
198
198
  regex_to_parse_score: A regular expression to parse score.
199
+ output_reasoning_text: If True, store the evaluator's reasoning content
200
+ as `llm_score_reasoning_text` in the instance details.
199
201
 
200
202
  Examples:
201
203
  >>> from flexeval import LLMScore, OpenAIChatAPI, Jinja2PromptTemplate
@@ -231,6 +233,7 @@ class LLMScore(Metric):
231
233
  category_key: str | list[str] | None = None,
232
234
  metric_prefix: str | None = None,
233
235
  regex_to_parse_score: str = r"(\d+)",
236
+ output_reasoning_text: bool = False,
234
237
  ) -> None:
235
238
  self.language_model = language_model
236
239
  self.prompt_template = prompt_template
@@ -240,6 +243,7 @@ class LLMScore(Metric):
240
243
  self.category_key = category_key
241
244
  self.metric_prefix = f"{metric_prefix}-" if metric_prefix else ""
242
245
  self.regex_to_parse_score = regex_to_parse_score
246
+ self.output_reasoning_text = output_reasoning_text
243
247
 
244
248
  def evaluate(
245
249
  self,
@@ -287,6 +291,11 @@ class LLMScore(Metric):
287
291
  f"{self.metric_prefix}llm_score_input": eval_in,
288
292
  f"{self.metric_prefix}llm_score_output": eval_out.text,
289
293
  }
294
+ | (
295
+ {f"{self.metric_prefix}llm_score_reasoning_text": eval_out.reasoning_text}
296
+ if self.output_reasoning_text
297
+ else {}
298
+ )
290
299
  for eval_score, eval_in, eval_out in zip(
291
300
  evaluator_score_list,
292
301
  evaluator_input_list,
@@ -320,6 +329,8 @@ class ChatLLMScore(Metric):
320
329
  The category keys are expected to be in extra_info.
321
330
  metric_prefix: A prefix to be added to the metric keys in the summary and instance details.
322
331
  regex_to_parse_score: A regular expression to parse score.
332
+ output_reasoning_text: If True, store the evaluator's reasoning content
333
+ as `llm_score_reasoning_text` in the instance details.
323
334
 
324
335
  Examples:
325
336
  >>> from flexeval import ChatLLMScore, OpenAIChatAPI, Jinja2PromptTemplate
@@ -357,6 +368,7 @@ class ChatLLMScore(Metric):
357
368
  category_key: str | list[str] | None = None,
358
369
  metric_prefix: str | None = None,
359
370
  regex_to_parse_score: str = r"(\d+)",
371
+ output_reasoning_text: bool = False,
360
372
  ) -> None:
361
373
  self.language_model = language_model
362
374
  self.prompt_template = prompt_template
@@ -367,6 +379,7 @@ class ChatLLMScore(Metric):
367
379
  self.category_key = category_key
368
380
  self.metric_prefix = f"{metric_prefix}-" if metric_prefix else ""
369
381
  self.regex_to_parse_score = regex_to_parse_score
382
+ self.output_reasoning_text = output_reasoning_text
370
383
 
371
384
  def evaluate(
372
385
  self,
@@ -412,6 +425,11 @@ class ChatLLMScore(Metric):
412
425
  f"{self.metric_prefix}llm_score_input": eval_in,
413
426
  f"{self.metric_prefix}llm_score_output": eval_out.text,
414
427
  }
428
+ | (
429
+ {f"{self.metric_prefix}llm_score_reasoning_text": eval_out.reasoning_text}
430
+ if self.output_reasoning_text
431
+ else {}
432
+ )
415
433
  for eval_score, eval_in, eval_out in zip(
416
434
  evaluator_score_list,
417
435
  evaluator_input_list,
@@ -4,7 +4,7 @@ from dataclasses import asdict, dataclass
4
4
  from enum import Enum
5
5
  from typing import Any
6
6
 
7
- from flexeval.core.language_model.base import LanguageModel
7
+ from flexeval.core.language_model.base import LanguageModel, LMOutput
8
8
  from flexeval.core.prompt_template.base import PromptTemplate
9
9
  from flexeval.core.reward_bench_dataset import RewardBenchInstance
10
10
  from flexeval.core.reward_model.base import RewardModel
@@ -40,38 +40,48 @@ def evaluate_model_output(model_output: str, gold_label: PairwiseChoice) -> bool
40
40
 
41
41
  def aggregate_judge_results(
42
42
  outputs: list[dict],
43
- judge_outputs: list,
43
+ judge_outputs: list[LMOutput],
44
44
  chosen_is_better_list: list[bool],
45
+ output_reasoning_text: bool = False,
45
46
  ) -> tuple[list[bool], list[dict]]:
46
47
  """
47
48
  Aggregates the doubled-length `judge_outputs` and `chosen_is_better_list`
48
49
  into final results for each individual instance.
49
50
 
51
+ Args:
52
+ outputs: Per-instance details to merge into the aggregated outputs.
53
+ judge_outputs: Judge outputs, twice the length of `outputs`.
54
+ chosen_is_better_list: Whether the chosen answer won, twice the length of `outputs`.
55
+ output_reasoning_text: If True, add `llm_reasoning_texts` to the aggregated outputs.
56
+
50
57
  Returns:
51
58
  final_results: list[bool]
52
59
  A list indicating whether each instance is ultimately judged as correct.
60
+ aggregated_outputs: list[dict]
61
+ A list of per-instance details.
53
62
  """
54
63
  aggregated_results: list[bool] = []
55
64
  aggregated_outputs: list[dict] = []
56
65
 
57
66
  for i, output in enumerate(outputs):
58
- ab_output_text = judge_outputs[i * 2].text
59
- ba_output_text = judge_outputs[i * 2 + 1].text
67
+ ab_output = judge_outputs[i * 2]
68
+ ba_output = judge_outputs[i * 2 + 1]
60
69
  ab_eval = chosen_is_better_list[i * 2]
61
70
  ba_eval = chosen_is_better_list[i * 2 + 1]
62
71
 
63
72
  consistent = ab_eval == ba_eval
64
73
  is_correct = consistent and ab_eval
65
74
 
66
- aggregated_outputs.append(
67
- {
68
- "llm_outputs": [ab_output_text, ba_output_text],
69
- "evaluation_results": [ab_eval, ba_eval],
70
- "consistent": consistent,
71
- "is_correct": is_correct,
72
- **output,
73
- }
74
- )
75
+ aggregated_output = {
76
+ "llm_outputs": [ab_output.text, ba_output.text],
77
+ "evaluation_results": [ab_eval, ba_eval],
78
+ "consistent": consistent,
79
+ "is_correct": is_correct,
80
+ **output,
81
+ }
82
+ if output_reasoning_text:
83
+ aggregated_output["llm_reasoning_texts"] = [ab_output.reasoning_text, ba_output.reasoning_text]
84
+ aggregated_outputs.append(aggregated_output)
75
85
  aggregated_results.append(is_correct)
76
86
 
77
87
  return aggregated_results, aggregated_outputs
@@ -102,6 +112,8 @@ class PairwiseJudgeRewardModel(RewardModel):
102
112
  Be sure to include {{prompt}}, {{answer_a}}, and {{answer_b}}.
103
113
  system_message: The system message to prepend to the chat messages.
104
114
  gen_kwargs: Generation kwargs for the language model.
115
+ output_reasoning_text: If True, store the judge's reasoning content
116
+ as `llm_reasoning_texts` in the outputs.
105
117
  """
106
118
 
107
119
  def __init__(
@@ -110,6 +122,7 @@ class PairwiseJudgeRewardModel(RewardModel):
110
122
  prompt_template: PromptTemplate,
111
123
  system_message: str | PromptTemplate | None = None,
112
124
  gen_kwargs: dict[str, Any] | None = None,
125
+ output_reasoning_text: bool = False,
113
126
  ) -> None:
114
127
  if gen_kwargs is None:
115
128
  gen_kwargs = {}
@@ -117,6 +130,7 @@ class PairwiseJudgeRewardModel(RewardModel):
117
130
  self.prompt_template = prompt_template
118
131
  self.system_message = system_message
119
132
  self.gen_kwargs = gen_kwargs
133
+ self.output_reasoning_text = output_reasoning_text
120
134
 
121
135
  def _create_input_chat_messages_list(self, pairwise_instance: PairwiseInstance) -> list[dict[str, str]]:
122
136
  pairwise_instance_asdict = asdict(pairwise_instance)
@@ -182,6 +196,8 @@ class PairwiseJudgeRewardModel(RewardModel):
182
196
  msg = "The number of outputs should be twice the number of inputs."
183
197
  raise ValueError(msg)
184
198
 
185
- aggregated_results, aggregated_outputs = aggregate_judge_results(outputs, judge_outputs, chosen_is_better_list)
199
+ aggregated_results, aggregated_outputs = aggregate_judge_results(
200
+ outputs, judge_outputs, chosen_is_better_list, self.output_reasoning_text
201
+ )
186
202
 
187
203
  return aggregated_results, aggregated_outputs
@@ -1,5 +1,6 @@
1
1
  from .aio import AIONormalizer
2
2
  from .base import StringProcessor
3
+ from .last_choice import LastChoiceExtractor
3
4
  from .last_line import LastLineExtractor
4
5
  from .lower import StringLower
5
6
  from .mgsm import RemoveCommaProcessor, SimpleEvalMGSMProcessor
@@ -0,0 +1,81 @@
1
+ import re
2
+ import string
3
+ from typing import Literal
4
+
5
+ from .base import StringProcessor
6
+
7
+
8
+ class LastChoiceExtractor(StringProcessor):
9
+ """
10
+ StringProcessor that extracts the last mentioned multiple-choice option symbol.
11
+ Useful to extract an answer after a step-by-step derivation.
12
+
13
+ MCQ benchmark scores (e.g. MMMU/JMMMU `mcq_exact_match`) depend on this
14
+ extraction logic; changing it silently shifts reported scores, so keep it
15
+ stable across releases.
16
+
17
+ Args:
18
+ lang: Language of the response, "en" or "ja".
19
+ With "ja", option symbols surrounded by Japanese characters
20
+ (e.g. "正解はAです。") are also detected.
21
+ option_type: The style of option symbols:
22
+ "number" (1, 2, ...), "alphabet" (a, b, ...), or "Alphabet" (A, B, ...).
23
+ max_num_options: The maximum number of options.
24
+
25
+ Examples:
26
+ >>> from flexeval import LastChoiceExtractor
27
+ >>> processor = LastChoiceExtractor(lang="en")
28
+ >>> text = "The answer is A."
29
+ >>> print(processor(text))
30
+ A
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ lang: Literal["ja", "en"] = "en",
36
+ option_type: Literal["number", "alphabet", "Alphabet"] = "Alphabet",
37
+ max_num_options: int = 4,
38
+ ) -> None:
39
+ self.lang = lang
40
+ if option_type == "number":
41
+ self.choices = [str(i) for i in range(1, max_num_options + 1)]
42
+ elif option_type == "alphabet":
43
+ self.choices = list(string.ascii_lowercase[:max_num_options])
44
+ elif option_type == "Alphabet":
45
+ self.choices = list(string.ascii_uppercase[:max_num_options])
46
+ else:
47
+ msg = f"Unsupported option_type: {option_type}"
48
+ raise ValueError(msg)
49
+
50
+ def _remove_punctuations(self, response: str) -> str:
51
+ punctuation = string.punctuation
52
+ if self.lang == "ja":
53
+ # add full-width punctuations
54
+ punctuation += "!”#$%&’()*+,−./:;<=>?@[\]^_`{|}〜、。¥・"
55
+ return response.strip().strip(punctuation)
56
+
57
+ def _find_choice_with_brackets(self, response: str) -> list[str]:
58
+ # e.g. (A), (B), ...
59
+ return [c for c in self.choices if f"({c})" in response]
60
+
61
+ def _find_choice_without_brackets(self, response: str) -> list[str]:
62
+ # e.g. A, B, ...
63
+ return [c for c in self.choices if re.search(rf"\b{c}\b", response)]
64
+
65
+ def _find_choice_between_japanese(self, response: str) -> list[str]:
66
+ # e.g. 正解はAです。
67
+ japanese_char_pattern = r"[\u3040-\u30FF\u4E00-\u9FFF]"
68
+ return [c for c in self.choices if re.search(rf"{japanese_char_pattern}{c}{japanese_char_pattern}", response)]
69
+
70
+ def __call__(self, text: str) -> str:
71
+ candidates = self._find_choice_with_brackets(text)
72
+ if not candidates:
73
+ text = self._remove_punctuations(text)
74
+ candidates = self._find_choice_without_brackets(text)
75
+ if not candidates and self.lang == "ja":
76
+ candidates = self._find_choice_between_japanese(text)
77
+
78
+ if not candidates:
79
+ return ""
80
+ # choose the last mentioned option if multiple options are extracted
81
+ return max(candidates, key=text.rfind)