A unit call-sign is not a place
"Post 1-2" reached the geocoder, resolved against its talkgroup anchor and produced a confident pin in the right town for an event with no known location — while sitting in the same incident's `units` list the whole time. A plausible wrong pin is worse than no pin: nothing downstream can tell it is wrong. Extraction returns `location` and `units` from one pass, so a string in both is a misclassification, not two facts. Drop it before the geocoder sees it. Closes server-26#52. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
964343c819
commit
cc038e6326
@@ -282,6 +282,27 @@ def clean_location(value) -> Optional[str]:
|
||||
return s
|
||||
|
||||
|
||||
def location_is_unit(location, units) -> bool:
|
||||
"""
|
||||
True when a location label is really one of the incident's own unit
|
||||
call-signs.
|
||||
|
||||
Extraction returns `location` and `units` from the same pass, so a string
|
||||
appearing in both is a misclassification, not two facts. "Post 1-2" reached
|
||||
the geocoder that way, resolved against its talkgroup anchor, and produced a
|
||||
confident pin in the right town for an event that has no known location at
|
||||
all — worse than no pin, because nothing downstream can tell it is wrong.
|
||||
See server-26#52.
|
||||
|
||||
Public because `intelligence.py` applies it at extraction time, alongside
|
||||
`clean_location`, so the string never reaches the geocoder.
|
||||
"""
|
||||
key = _place_key(location)
|
||||
if not key:
|
||||
return False
|
||||
return any(key == _place_key(u) for u in (units or []))
|
||||
|
||||
|
||||
def _place_key(value) -> str:
|
||||
"""Case- and punctuation-blind key for comparing two location labels."""
|
||||
return re.sub(r"[^a-z0-9]+", " ", str(value or "").lower()).strip()
|
||||
|
||||
@@ -19,7 +19,7 @@ from app.internal import area_context
|
||||
# Location validity is defined once, by the module that owns the incident's
|
||||
# location/pin invariant. incident_correlator does not import this module, so
|
||||
# this is not a cycle.
|
||||
from app.internal.incident_correlator import clean_location
|
||||
from app.internal.incident_correlator import clean_location, location_is_unit
|
||||
|
||||
_PROMPT_TEMPLATE = """You are analyzing a P25 public safety radio recording. The audio was transcribed by Whisper through a digital radio vocoder, which introduces errors. Each numbered transmission is a separate PTT press from a different radio.
|
||||
|
||||
@@ -252,6 +252,18 @@ async def extract_scenes(
|
||||
location: Optional[str] = clean_location(scene.get("location"))
|
||||
vehicles: list[str] = scene.get("vehicles") or []
|
||||
units: list[str] = scene.get("units") or []
|
||||
# A "location" that is also one of this scene's own units is a unit
|
||||
# call-sign, not a place. Both lists come from the same extraction pass,
|
||||
# so the disagreement is free to detect and the string must be dropped
|
||||
# before it reaches the geocoder — anchored place verification will
|
||||
# otherwise resolve "Post 1-2" to a confident, plausible, wrong pin in
|
||||
# the right town. See server-26#52.
|
||||
if location and location_is_unit(location, units):
|
||||
logger.info(
|
||||
f"Intelligence: dropping location {location!r} — it is one of "
|
||||
f"this scene's units, not a place"
|
||||
)
|
||||
location = None
|
||||
cleared_units: list[str] = scene.get("cleared_units") or []
|
||||
# Every call carries a severity — it is the signal the correlator uses to
|
||||
# decide whether a call is incident-worthy at all, so it must never be
|
||||
|
||||
Reference in New Issue
Block a user