|
1 | 1 | from typing import Any, Tuple, List, Dict |
| 2 | +import json |
2 | 3 | import yaml |
3 | 4 |
|
4 | 5 | from kubernetes.dynamic import DynamicClient |
@@ -1145,3 +1146,129 @@ def get_excluded_model_str(models: list[str]) -> str: |
1145 | 1146 | - {model_name} |
1146 | 1147 | """ |
1147 | 1148 | return excluded_models |
| 1149 | + |
| 1150 | + |
| 1151 | +def extract_custom_property_values(custom_properties: dict[str, Any]) -> dict[str, str]: |
| 1152 | + """ |
| 1153 | + Extract string values from MetadataStringValue format for custom properties. |
| 1154 | +
|
| 1155 | + Args: |
| 1156 | + custom_properties: Dictionary of custom properties from API response |
| 1157 | +
|
| 1158 | + Returns: |
| 1159 | + Dictionary of extracted string values for size, tensor_type, variant_group_id |
| 1160 | + """ |
| 1161 | + extracted = {} |
| 1162 | + expected_keys = ["size", "tensor_type", "variant_group_id"] |
| 1163 | + |
| 1164 | + for key in expected_keys: |
| 1165 | + if key in custom_properties: |
| 1166 | + prop_data = custom_properties[key] |
| 1167 | + if isinstance(prop_data, dict) and "string_value" in prop_data: |
| 1168 | + extracted[key] = prop_data["string_value"] |
| 1169 | + else: |
| 1170 | + LOGGER.warning(f"Unexpected format for custom property '{key}': {prop_data}") |
| 1171 | + |
| 1172 | + LOGGER.info(f"Extracted {len(extracted)} custom properties: {list(extracted.keys())}") |
| 1173 | + return extracted |
| 1174 | + |
| 1175 | + |
| 1176 | +def validate_custom_properties_structure(custom_properties: dict[str, Any]) -> bool: |
| 1177 | + """ |
| 1178 | + Validate that custom properties follow the expected MetadataStringValue structure. |
| 1179 | +
|
| 1180 | + Args: |
| 1181 | + custom_properties: Dictionary of custom properties from API response |
| 1182 | +
|
| 1183 | + Returns: |
| 1184 | + True if all custom properties have valid structure, False otherwise |
| 1185 | + """ |
| 1186 | + if not custom_properties: |
| 1187 | + LOGGER.info("No custom properties found - structure validation skipped") |
| 1188 | + return True |
| 1189 | + |
| 1190 | + expected_keys = ["size", "tensor_type", "variant_group_id"] |
| 1191 | + |
| 1192 | + for key in expected_keys: |
| 1193 | + if key in custom_properties: |
| 1194 | + prop_data = custom_properties[key] |
| 1195 | + |
| 1196 | + if not isinstance(prop_data, dict): |
| 1197 | + LOGGER.error(f"Custom property '{key}' is not a dictionary: {prop_data}") |
| 1198 | + return False |
| 1199 | + |
| 1200 | + if "metadataType" not in prop_data: |
| 1201 | + LOGGER.error(f"Custom property '{key}' missing 'metadataType' field") |
| 1202 | + return False |
| 1203 | + |
| 1204 | + if prop_data.get("metadataType") != "MetadataStringValue": |
| 1205 | + LOGGER.error(f"Custom property '{key}' has unexpected metadataType: {prop_data.get('metadataType')}") |
| 1206 | + return False |
| 1207 | + |
| 1208 | + if "string_value" not in prop_data: |
| 1209 | + LOGGER.error(f"Custom property '{key}' missing 'string_value' field") |
| 1210 | + return False |
| 1211 | + |
| 1212 | + if not isinstance(prop_data.get("string_value"), str): |
| 1213 | + LOGGER.error(f"Custom property '{key}' string_value is not a string: {prop_data.get('string_value')}") |
| 1214 | + return False |
| 1215 | + |
| 1216 | + LOGGER.info(f"Custom property '{key}' has valid structure: '{prop_data.get('string_value')}'") |
| 1217 | + |
| 1218 | + LOGGER.info("All custom properties have valid structure") |
| 1219 | + return True |
| 1220 | + |
| 1221 | + |
| 1222 | +def validate_custom_properties_match_metadata(api_custom_properties: dict[str, str], metadata: dict[str, Any]) -> bool: |
| 1223 | + """ |
| 1224 | + Compare API custom properties with metadata.json values. |
| 1225 | +
|
| 1226 | + Args: |
| 1227 | + api_custom_properties: Extracted custom properties from API (string values) |
| 1228 | + metadata: Parsed metadata.json content |
| 1229 | +
|
| 1230 | + Returns: |
| 1231 | + True if all custom properties match metadata values, False otherwise |
| 1232 | + """ |
| 1233 | + expected_keys = ["size", "tensor_type", "variant_group_id"] |
| 1234 | + |
| 1235 | + for key in expected_keys: |
| 1236 | + api_value = api_custom_properties.get(key) |
| 1237 | + metadata_value = metadata.get(key) |
| 1238 | + |
| 1239 | + if api_value != metadata_value: |
| 1240 | + LOGGER.error(f"Mismatch for custom property '{key}': API='{api_value}' vs metadata='{metadata_value}'") |
| 1241 | + return False |
| 1242 | + |
| 1243 | + if api_value is not None: # Only log if the property exists |
| 1244 | + LOGGER.info(f"Custom property '{key}' matches: '{api_value}'") |
| 1245 | + |
| 1246 | + LOGGER.info("All custom properties match metadata.json values") |
| 1247 | + return True |
| 1248 | + |
| 1249 | + |
| 1250 | +def get_metadata_from_catalog_pod(model_catalog_pod: Pod, model_name: str) -> dict[str, Any]: |
| 1251 | + """ |
| 1252 | + Read and parse metadata.json for a model from the catalog pod. |
| 1253 | +
|
| 1254 | + Args: |
| 1255 | + model_catalog_pod: The catalog pod instance |
| 1256 | + model_name: Name of the model |
| 1257 | +
|
| 1258 | + Returns: |
| 1259 | + Parsed metadata.json content |
| 1260 | +
|
| 1261 | + Raises: |
| 1262 | + Exception: If metadata.json cannot be read or parsed |
| 1263 | + """ |
| 1264 | + metadata_path = f"/shared-benchmark-data/{model_name}/metadata.json" |
| 1265 | + LOGGER.info(f"Reading metadata from: {metadata_path}") |
| 1266 | + |
| 1267 | + try: |
| 1268 | + metadata_json = model_catalog_pod.execute(command=["cat", metadata_path], container=CATALOG_CONTAINER) |
| 1269 | + metadata = json.loads(metadata_json) |
| 1270 | + LOGGER.info(f"Successfully loaded metadata.json for model '{model_name}'") |
| 1271 | + return metadata |
| 1272 | + except Exception as e: |
| 1273 | + LOGGER.error(f"Failed to read metadata.json for model '{model_name}': {e}") |
| 1274 | + raise |
0 commit comments