request_context.mojo (7487B)
1 from std.collections import List, Optional 2 3 from json import Value 4 from json.deserialize import get_bool, get_int, get_string 5 6 7 def _has_key(value: Value, key: String) -> Bool: 8 for candidate in value.object_keys(): 9 if candidate == key: 10 return True 11 return False 12 13 14 def _require_object(value: Value, context: String) raises: 15 if not value.is_object(): 16 raise Error(context + " must be a JSON object") 17 18 19 def _require_allowed_keys( 20 value: Value, allowed_keys: List[String], context: String 21 ) raises: 22 for key in value.object_keys(): 23 var allowed = False 24 for allowed_key in allowed_keys: 25 if key == allowed_key: 26 allowed = True 27 break 28 if not allowed: 29 raise Error(context + " contains unexpected field '" + key + "'") 30 31 32 def _require_non_empty(value: String, context: String) raises: 33 if value == "": 34 raise Error(context + " must not be empty") 35 36 37 def _require_positive_int(value: Int, context: String) raises: 38 if value <= 0: 39 raise Error(context + " must be greater than zero") 40 41 42 def _parse_string_list(value: Value, context: String) raises -> List[String]: 43 if value.is_null(): 44 return List[String]() 45 if not value.is_array(): 46 raise Error(context + " must be a JSON array") 47 48 var items = List[String]() 49 for item in value.array_items(): 50 if not item.is_string(): 51 raise Error(context + " must contain only strings") 52 _require_non_empty(item.string_value(), context + " item") 53 items.append(item.string_value()) 54 return items^ 55 56 57 @fieldwise_init 58 struct RequestScope(Copyable, Movable): 59 var listing_ids: List[String] 60 var farm_ids: List[String] 61 var account_ids: List[String] 62 var platform_ids: List[String] 63 var object_filters: Optional[Value] 64 65 66 @fieldwise_init 67 struct TimeRange(Copyable, Movable): 68 var start: String 69 var end: String 70 71 72 @fieldwise_init 73 struct RequestContext(Copyable, Movable): 74 var consumer: String 75 var execution_mode_preference: String 76 var deadline_ms: Int 77 var scope: Optional[RequestScope] 78 var time_range: Optional[TimeRange] 79 var evidence_limit: Int 80 var consistency: String 81 var return_provenance: Bool 82 var explain_plan: Bool 83 84 85 def request_context_feature_names() -> List[String]: 86 return request_context_allowed_keys() 87 88 89 def request_context_allowed_keys() -> List[String]: 90 var features = List[String]() 91 features.append("consumer") 92 features.append("execution_mode_preference") 93 features.append("deadline_ms") 94 features.append("scope") 95 features.append("time_range") 96 features.append("evidence_limit") 97 features.append("consistency") 98 features.append("return_provenance") 99 features.append("explain_plan") 100 return features^ 101 102 103 def accepted_request_context_feature_names() -> List[String]: 104 var features = List[String]() 105 features.append("consumer") 106 features.append("execution_mode_preference") 107 features.append("deadline_ms") 108 features.append("scope.listing_ids") 109 features.append("time_range.start") 110 features.append("time_range.end") 111 features.append("evidence_limit") 112 features.append("consistency") 113 features.append("return_provenance") 114 features.append("explain_plan") 115 return features^ 116 117 118 def effective_request_context_feature_names() -> List[String]: 119 var features = List[String]() 120 features.append("execution_mode_preference") 121 features.append("scope.listing_ids") 122 features.append("return_provenance") 123 return features^ 124 125 126 def default_request_context() -> RequestContext: 127 return RequestContext( 128 consumer="unknown", 129 execution_mode_preference="deterministic", 130 deadline_ms=2500, 131 scope=None, 132 time_range=None, 133 evidence_limit=10, 134 consistency="default", 135 return_provenance=False, 136 explain_plan=False, 137 ) 138 139 140 def assisted_execution_requested(context: RequestContext) -> Bool: 141 return context.execution_mode_preference == "assisted" 142 143 144 def _parse_scope(json: Value) raises -> RequestScope: 145 _require_object(json, "request context scope") 146 147 var allowed_keys = List[String]() 148 allowed_keys.append("listing_ids") 149 _require_allowed_keys(json, allowed_keys, "request context scope") 150 151 var listing_ids_json = Value(None) 152 if _has_key(json, "listing_ids"): 153 listing_ids_json = json["listing_ids"].clone() 154 155 return RequestScope( 156 listing_ids=_parse_string_list( 157 listing_ids_json, "request context scope listing_ids" 158 ), 159 farm_ids=List[String](), 160 account_ids=List[String](), 161 platform_ids=List[String](), 162 object_filters=None, 163 ) 164 165 166 def _parse_time_range(json: Value) raises -> TimeRange: 167 _require_object(json, "request context time_range") 168 169 var allowed_keys = List[String]() 170 allowed_keys.append("start") 171 allowed_keys.append("end") 172 _require_allowed_keys(json, allowed_keys, "request context time_range") 173 174 var start = get_string(json, "start") 175 _require_non_empty(start, "request context time_range start") 176 177 var end = get_string(json, "end") 178 _require_non_empty(end, "request context time_range end") 179 180 return TimeRange(start=start, end=end) 181 182 183 def parse_request_context(json: Value) raises -> RequestContext: 184 if json.is_null(): 185 return default_request_context() 186 187 _require_object(json, "request context") 188 189 var allowed_keys = request_context_allowed_keys() 190 _require_allowed_keys(json, allowed_keys, "request context") 191 192 var context = default_request_context() 193 194 if _has_key(json, "consumer"): 195 context.consumer = get_string(json, "consumer") 196 _require_non_empty(context.consumer, "request context consumer") 197 198 if _has_key(json, "execution_mode_preference"): 199 context.execution_mode_preference = get_string( 200 json, "execution_mode_preference" 201 ) 202 if ( 203 context.execution_mode_preference != "deterministic" 204 and context.execution_mode_preference != "assisted" 205 ): 206 raise Error( 207 "request context execution_mode_preference must be 'deterministic' or 'assisted'" 208 ) 209 210 if _has_key(json, "deadline_ms"): 211 context.deadline_ms = get_int(json, "deadline_ms") 212 _require_positive_int( 213 context.deadline_ms, "request context deadline_ms" 214 ) 215 216 if _has_key(json, "scope"): 217 var scope_json = json["scope"].clone() 218 if not scope_json.is_null(): 219 context.scope = _parse_scope(scope_json) 220 221 if _has_key(json, "time_range"): 222 var time_range_json = json["time_range"].clone() 223 if not time_range_json.is_null(): 224 context.time_range = _parse_time_range(time_range_json) 225 226 if _has_key(json, "evidence_limit"): 227 context.evidence_limit = get_int(json, "evidence_limit") 228 _require_positive_int( 229 context.evidence_limit, "request context evidence_limit" 230 ) 231 232 if _has_key(json, "consistency"): 233 context.consistency = get_string(json, "consistency") 234 _require_non_empty( 235 context.consistency, "request context consistency" 236 ) 237 238 if _has_key(json, "return_provenance"): 239 context.return_provenance = get_bool(json, "return_provenance") 240 241 if _has_key(json, "explain_plan"): 242 context.explain_plan = get_bool(json, "explain_plan") 243 244 return context^