result.mojo (4616B)
1 from std.collections import List 2 3 from json import Value, loads, validate 4 5 from hyf_core.capabilities.query_analysis import ( 6 ExtractedFilters, 7 QueryAnalysis, 8 ) 9 from hyf_provider.schema import query_rewrite_schema 10 11 12 @fieldwise_init 13 struct MaxLocalProviderStatus(Copyable, Movable): 14 var backend_kind: String 15 var provider: String 16 var route: String 17 var model: String 18 var reachable: Bool 19 var state: String 20 var reason: String 21 22 23 def _has_key(value: Value, key: String) -> Bool: 24 for candidate in value.object_keys(): 25 if candidate == key: 26 return True 27 return False 28 29 30 def _string_array(value: Value, context: String) raises -> List[String]: 31 if not value.is_array(): 32 raise Error(context + " must be an array") 33 34 var items = List[String]() 35 for item in value.array_items(): 36 if not item.is_string(): 37 raise Error(context + " items must be strings") 38 items.append(item.string_value()) 39 return items^ 40 41 42 def _first_validation_error(value: Value) raises -> String: 43 var validation = validate(value, query_rewrite_schema()) 44 if validation.valid: 45 return "" 46 if len(validation.errors) == 0: 47 return "query_rewrite structured output failed schema validation" 48 var error = validation.errors[0].copy() 49 if error.path == "": 50 return String(error.message) 51 return String(error.path) + ": " + String(error.message) 52 53 54 def extract_chat_completion_text(response: Value) raises -> String: 55 if not response.is_object(): 56 raise Error("provider_schema_invalid") 57 if _has_key(response, "error"): 58 raise Error("provider_error_payload") 59 if not _has_key(response, "choices"): 60 raise Error("provider_empty_choices") 61 if ( 62 not response["choices"].is_array() 63 or len(response["choices"].array_items()) == 0 64 ): 65 raise Error("provider_empty_choices") 66 67 var choice = response["choices"][0].clone() 68 if not choice.is_object() or not _has_key(choice, "message"): 69 raise Error("provider_missing_content") 70 71 var message = choice["message"].clone() 72 if not message.is_object(): 73 raise Error("provider_missing_content") 74 if not _has_key(message, "content"): 75 raise Error("provider_missing_content") 76 77 var content = message["content"].clone() 78 if content.is_string(): 79 return content.string_value() 80 81 if content.is_array(): 82 var collected = String("") 83 for part in content.array_items(): 84 if ( 85 part.is_object() 86 and _has_key(part, "type") 87 and part["type"].is_string() 88 and part["type"].string_value() == "text" 89 and _has_key(part, "text") 90 and part["text"].is_string() 91 ): 92 collected += part["text"].string_value() 93 94 if collected != "": 95 return collected^ 96 97 raise Error("provider_missing_content") 98 99 100 def parse_query_analysis_json(value: Value) raises -> QueryAnalysis: 101 if not value.is_object(): 102 raise Error("provider_schema_invalid") 103 104 var validation_error = _first_validation_error(value.clone()) 105 if validation_error != "": 106 raise Error("provider_schema_invalid") 107 108 try: 109 var filters = value["extracted_filters"].clone() 110 return QueryAnalysis( 111 original_text=value["original_text"].string_value(), 112 normalized_text=value["normalized_text"].string_value(), 113 rewritten_text=value["rewritten_text"].string_value(), 114 query_terms=_string_array(value["query_terms"], "query_terms"), 115 normalization_signals=_string_array( 116 value["normalization_signals"], "normalization_signals" 117 ), 118 ranking_hints=_string_array( 119 value["ranking_hints"], "ranking_hints" 120 ), 121 extracted_filters=ExtractedFilters( 122 local_intent=filters["local_intent"].bool_value(), 123 fulfillment=filters["fulfillment"].string_value(), 124 time_window=filters["time_window"].string_value(), 125 ), 126 ) 127 except: 128 raise Error("provider_schema_invalid") 129 130 131 def _load_query_analysis_content_json(text: String) raises -> Value: 132 try: 133 return loads(text) 134 except: 135 raise Error("provider_invalid_json") 136 137 138 def parse_query_analysis_from_chat_completion( 139 response: Value, 140 ) raises -> QueryAnalysis: 141 var text = extract_chat_completion_text(response) 142 return parse_query_analysis_json(_load_query_analysis_content_json(text))