Add debugging

This commit is contained in:
Logan
2026-05-04 01:46:56 -04:00
parent e704df1a62
commit 97f4286810
3 changed files with 70 additions and 2 deletions
@@ -128,6 +128,7 @@ async def correlate_call(
coords: Optional[dict] = location_coords or call_doc.get("location_coords")
matched_incident: Optional[dict] = None
corr_debug: dict = {}
# A "thin" call carries no scene-identifying information — it is a pure
# status transmission (10-4, en route, acknowledgement). Detected by the
@@ -170,6 +171,10 @@ async def correlate_call(
# Status/ack call — no scene data to reason about.
# Attach to whichever recent incident was most recently active on this TGID.
matched_incident = max(tg_recent, key=lambda inc: inc.get("updated_at", ""))
corr_debug = {
"corr_path": "fast/thin",
"corr_incident_idle_min": round(_incident_idle_minutes(matched_incident, now), 1),
}
logger.info(
f"Correlator fast-path (thin→last TGID incident): "
f"call {call_id}{matched_incident['incident_id']}"
@@ -181,6 +186,10 @@ async def correlate_call(
settings.location_proximity_km, is_dispatch=is_dispatch,
):
matched_incident = candidate
corr_debug = {
"corr_path": "fast/single",
"corr_incident_idle_min": round(_incident_idle_minutes(candidate, now), 1),
}
logger.info(
f"Correlator fast-path: call {call_id}{candidate['incident_id']}"
)
@@ -193,6 +202,11 @@ async def correlate_call(
matched_incident = _disambiguate(
tg_recent, call_units, call_vehicles, coords, call_embedding
)
corr_debug = {
"corr_path": "fast/disambig",
"corr_incident_idle_min": round(_incident_idle_minutes(matched_incident, now), 1),
"corr_candidates": len(tg_recent),
}
logger.info(
f"Correlator fast-path (disambig {len(tg_recent)} candidates): "
f"call {call_id}{matched_incident['incident_id']}"
@@ -210,6 +224,7 @@ async def correlate_call(
)
if dist_km <= settings.location_proximity_km:
matched_incident = inc
corr_debug = {"corr_path": "location", "corr_distance_km": round(dist_km, 3)}
logger.info(
f"Correlator location-path: call {call_id}{inc['incident_id']} "
f"(dist={dist_km:.2f}km)"
@@ -246,10 +261,15 @@ async def correlate_call(
best_cross_inc = inc
if best_cross_inc and best_cross_score >= settings.embedding_cross_tg_threshold:
matched_incident = best_cross_inc
shared = len(call_unit_set & set(best_cross_inc.get("units") or []))
corr_debug = {
"corr_path": "cross-tg",
"corr_score": round(best_cross_score, 4),
"corr_shared_units": shared,
}
logger.info(
f"Correlator cross-TG path: call {call_id}{best_cross_inc['incident_id']} "
f"(sim={best_cross_score:.3f}, "
f"shared_units={len(call_unit_set & set(best_cross_inc.get('units') or []))})"
f"(sim={best_cross_score:.3f}, shared_units={shared})"
)
# ── 3. Slow path: embedding similarity (time-limited, same type) ──────────
@@ -282,6 +302,11 @@ async def correlate_call(
)
if dist_km <= settings.location_proximity_km * 4:
matched_incident = best_inc
corr_debug = {
"corr_path": "slow",
"corr_score": round(best_score, 4),
"corr_distance_km": round(dist_km, 3),
}
logger.info(
f"Correlator slow-path: call {call_id}{best_inc['incident_id']} "
f"(sim={best_score:.3f}, dist={dist_km:.2f}km)"
@@ -290,6 +315,10 @@ async def correlate_call(
# High-confidence semantic match; geocode unavailable on one or
# both sides — content similarity alone is sufficient evidence.
matched_incident = best_inc
corr_debug = {
"corr_path": "slow/no-location",
"corr_score": round(best_score, 4),
}
logger.info(
f"Correlator slow-path (high-confidence, no location): "
f"call {call_id}{best_inc['incident_id']} (sim={best_score:.3f})"
@@ -309,10 +338,19 @@ async def correlate_call(
tags, location, location_coords,
call_units, call_vehicles, call_embedding, call_severity, now,
)
corr_debug["corr_path"] = "new"
else:
# No match and either no type or creation suppressed — nothing to do
return None
# Persist the correlation decision to the call document so it can be
# inspected in Firestore or the admin UI without log-scraping.
if corr_debug:
try:
await fstore.doc_set("calls", call_id, corr_debug)
except Exception as e:
logger.warning(f"Could not write corr_debug for call {call_id}: {e}")
return incident_id
+23
View File
@@ -138,6 +138,29 @@ export function CallRow({ call, systemName, isAdmin }: Props) {
</div>
)}
{/* Correlation debug — admin only */}
{isAdmin && call.corr_path && (
<div className="flex flex-wrap gap-x-3 gap-y-0.5 text-xs font-mono text-gray-600">
<span>corr:</span>
<span className="text-gray-400">{call.corr_path}</span>
{call.corr_incident_idle_min != null && (
<span>idle {call.corr_incident_idle_min}min</span>
)}
{call.corr_score != null && (
<span>sim={call.corr_score.toFixed(3)}</span>
)}
{call.corr_distance_km != null && (
<span>dist={call.corr_distance_km}km</span>
)}
{call.corr_shared_units != null && (
<span>{call.corr_shared_units} shared units</span>
)}
{call.corr_candidates != null && (
<span>{call.corr_candidates} candidates</span>
)}
</div>
)}
{/* Transcript */}
{editing ? (
<div className="space-y-2" onClick={(e) => e.stopPropagation()}>
+7
View File
@@ -56,6 +56,13 @@ export interface CallRecord {
location: string | null;
tags: string[];
status: "active" | "ended";
// Correlation debug — written by the correlator, present after a call is linked
corr_path?: string | null;
corr_score?: number | null;
corr_distance_km?: number | null;
corr_incident_idle_min?: number | null;
corr_shared_units?: number | null;
corr_candidates?: number | null;
}
export interface IncidentRecord {