jev_answers.mojo (5906B)
1 from std.collections import List 2 3 from json import Value 4 5 from hyf_assist.evaluator import ( 6 TypedAnswer, 7 typed_choice, 8 typed_noul, 9 typed_score, 10 ) 11 12 13 def _has_key(value: Value, key: String) -> Bool: 14 for candidate in value.object_keys(): 15 if candidate == key: 16 return True 17 return False 18 19 20 def parse_noul_answer(question_id: String, value: Value) raises -> TypedAnswer: 21 if not value.is_object() or not _has_key(value, "type"): 22 raise Error("provider_answer_invalid") 23 if value["type"].string_value() != "noul": 24 raise Error("provider_answer_wrong_type") 25 if not _has_key(value, "noul") or not value["noul"].is_float(): 26 raise Error("provider_answer_invalid") 27 var probability = value["noul"].float_value() 28 if probability < 0.0 or probability > 1.0: 29 raise Error("provider_answer_noul_out_of_range") 30 return typed_noul(question_id, probability) 31 32 33 def parse_choice_answer( 34 question_id: String, value: Value, choices: List[String] 35 ) raises -> TypedAnswer: 36 if not value.is_object() or not _has_key(value, "type"): 37 raise Error("provider_answer_invalid") 38 if value["type"].string_value() != "choice": 39 raise Error("provider_answer_wrong_type") 40 if not _has_key(value, "choice"): 41 raise Error("provider_answer_invalid") 42 var selected = value["choice"].string_value() 43 var known = False 44 for choice in choices: 45 if choice == selected: 46 known = True 47 if not known: 48 raise Error("provider_answer_unknown_choice") 49 if not _has_key(value, "confidence") or not value["confidence"].is_float(): 50 raise Error("provider_answer_invalid") 51 var confidence = value["confidence"].float_value() 52 if confidence < 0.0 or confidence > 1.0: 53 raise Error("provider_answer_confidence_out_of_range") 54 if _has_key(value, "probabilities"): 55 var probabilities = value["probabilities"] 56 if not probabilities.is_object(): 57 raise Error("provider_answer_invalid") 58 var total = 0.0 59 for choice in choices: 60 if not _has_key(probabilities, choice): 61 raise Error("provider_answer_missing_probability") 62 total += probabilities[choice].float_value() 63 if total < 0.999 or total > 1.001: 64 raise Error("provider_answer_bad_distribution_sum") 65 return typed_choice(question_id, selected, confidence) 66 67 68 def parse_score_answer( 69 question_id: String, value: Value, rubric: List[String] 70 ) raises -> TypedAnswer: 71 if not value.is_object() or not _has_key(value, "type"): 72 raise Error("provider_answer_invalid") 73 if value["type"].string_value() != "score": 74 raise Error("provider_answer_wrong_type") 75 if not _has_key(value, "score"): 76 raise Error("provider_answer_invalid") 77 var score_value = value["score"] 78 var raw_score = 0.0 79 if score_value.is_int(): 80 raw_score = Float64(score_value.int_value()) 81 elif score_value.is_float(): 82 raw_score = score_value.float_value() 83 else: 84 raise Error("provider_answer_invalid") 85 var score = Int(raw_score) 86 if Float64(score) != raw_score: 87 raise Error("provider_answer_invalid") 88 if score < 0 or score >= len(rubric): 89 raise Error("provider_answer_score_out_of_range") 90 if not _has_key(value, "confidence") or not value["confidence"].is_float(): 91 raise Error("provider_answer_invalid") 92 var confidence = value["confidence"].float_value() 93 if confidence < 0.0 or confidence > 1.0: 94 raise Error("provider_answer_confidence_out_of_range") 95 if _has_key(value, "legend"): 96 var legend = value["legend"] 97 if not legend.is_object() or len(legend.object_keys()) != len(rubric): 98 raise Error("provider_answer_wrong_legend") 99 if _has_key(value, "probabilities"): 100 var probabilities = value["probabilities"] 101 if not probabilities.is_object() or len( 102 probabilities.object_keys() 103 ) != len(rubric): 104 raise Error("provider_answer_missing_level") 105 var total = 0.0 106 for level in range(len(rubric)): 107 var key = String(level) 108 if not _has_key(probabilities, key): 109 raise Error("provider_answer_missing_level") 110 total += probabilities[key].float_value() 111 if total < 0.999 or total > 1.001: 112 raise Error("provider_answer_bad_distribution_sum") 113 return typed_score(question_id, score, len(rubric), confidence) 114 115 116 from hyf_assist.questions import QuestionBundle 117 118 119 def parse_jev_response( 120 body: Value, bundle: QuestionBundle 121 ) raises -> List[TypedAnswer]: 122 if not body.is_object(): 123 raise Error("provider_response_invalid") 124 if not _has_key(body, "model") or not _has_key(body, "answers"): 125 raise Error("provider_response_invalid") 126 if body["model"].string_value() != bundle.model: 127 raise Error("provider_model_mismatch") 128 var answers = body["answers"] 129 if not answers.is_object(): 130 raise Error("provider_response_invalid") 131 132 var result = List[TypedAnswer]() 133 for question in bundle.questions: 134 if not _has_key(answers, question.id): 135 raise Error("provider_answer_missing") 136 var answer = answers[question.id] 137 if question.kind == "choice": 138 result.append( 139 parse_choice_answer(question.id, answer, question.choices) 140 ) 141 elif question.kind == "noul": 142 result.append(parse_noul_answer(question.id, answer)) 143 elif question.kind == "score": 144 result.append( 145 parse_score_answer(question.id, answer, question.rubric) 146 ) 147 else: 148 raise Error("provider_answer_wrong_type") 149 150 if len(answers.object_keys()) != len(bundle.questions): 151 raise Error("provider_answer_extra") 152 return result^