/** * gptMapping.js — canonical GPT-raw → features field mapping. * * Replaces 4 identical inline mapping blocks across main.js, client.js, * and tracker.js (2 locations). */ APP.core.gptMapping = {}; /** Frozen assessment-status string constants. */ APP.core.gptMapping.STATUS = Object.freeze({ ASSESSED: "ASSESSED", UNASSESSED: "UNASSESSED", STALE: "STALE", PENDING_GPT: "PENDING_GPT", }); /** * Build a features object from a gpt_raw payload. * * @param {Object|null|undefined} gptRaw - The gpt_raw dict from a detection. * @returns {Object} Features key-value map (empty object if gptRaw is falsy). */ APP.core.gptMapping.buildFeatures = function (gptRaw) { if (!gptRaw) return {}; const rangeStr = gptRaw.range_estimate && gptRaw.range_estimate !== "Unknown" ? gptRaw.range_estimate + " (est.)" : "Unknown"; const features = { "Type": gptRaw.object_type || "Unknown", "Size": gptRaw.size || "Unknown", "Threat Lvl": (gptRaw.threat_level || gptRaw.threat_level_score || "?") + "/10", "Status": gptRaw.threat_classification || "?", "Weapons": (gptRaw.visible_weapons || []).join(", ") || "None Visible", "Readiness": gptRaw.weapon_readiness || "Unknown", "Motion": gptRaw.motion_status || "Unknown", "Range": rangeStr, "Bearing": gptRaw.bearing || "Unknown", "Intent": gptRaw.tactical_intent || "Unknown", }; const dynFeats = gptRaw.dynamic_features || []; for (const feat of dynFeats) { if (feat && feat.key && feat.value) { features[feat.key] = feat.value; } } return features; };