Merge feat/297-cheaper-ai into feat/299-correlation-simplify
Resolve upload.py (no-scene fallback uses the new _correlate with #298's non_event_chatter reason) and chatter_classifier docstring (gate built, off by default). Fold intelligence._llm_bool into incident_correlator.parse_llm_bool (#88). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,27 @@ async def list_alerts(
|
||||
return await fstore.collection_list("alert_events", **filters)
|
||||
|
||||
|
||||
@router.get("/alerts/held")
|
||||
async def list_held_alerts(
|
||||
status: Optional[str] = Query("held", description="held | released | superseded | rule_gone; empty = all"),
|
||||
org_id: Optional[str] = Query(None, description="Platform-admin only — omit for every org."),
|
||||
_: dict = Depends(require_admin_token),
|
||||
):
|
||||
"""
|
||||
Rule matches the confidence gate kept from being pushed (server-26#300).
|
||||
Admin-only: a held match is by definition one we do not trust enough to
|
||||
show, so it does not belong on any customer surface (and alert_holds has
|
||||
no Firestore rule, so the browser cannot read it directly either).
|
||||
"""
|
||||
filters = {}
|
||||
if status:
|
||||
filters["status"] = status
|
||||
if org_id:
|
||||
filters["org_id"] = org_id
|
||||
holds = await fstore.collection_list("alert_holds", **filters)
|
||||
return sorted(holds, key=lambda h: h.get("triggered_at") or "", reverse=True)
|
||||
|
||||
|
||||
@router.post("/alerts/{alert_id}/acknowledge")
|
||||
async def acknowledge_alert(alert_id: str, decoded: dict = Depends(require_service_or_firebase_token)):
|
||||
doc = await fstore.doc_get("alert_events", alert_id)
|
||||
|
||||
@@ -454,7 +454,10 @@ async def _extract_and_correlate(
|
||||
if not scenes:
|
||||
_call_doc = await fstore.doc_get("calls", call_id)
|
||||
skip_reason = (_call_doc or {}).get("skip_reason")
|
||||
if not skip_reason or skip_reason == "transcript_too_short":
|
||||
# non_event_chatter (server-26#298) is the same population as
|
||||
# transcript_too_short, just longer: housekeeping that skipped the
|
||||
# paid extraction but may still be a same-talkgroup follow-up.
|
||||
if not skip_reason or skip_reason in ("transcript_too_short", "non_event_chatter"):
|
||||
incident_id, _ = await _correlate(
|
||||
call_id=call_id,
|
||||
node_id=node_id,
|
||||
@@ -465,6 +468,11 @@ async def _extract_and_correlate(
|
||||
incident_type=None,
|
||||
location=None,
|
||||
location_coords=None,
|
||||
# server-26#104: the call doc's own severity, as
|
||||
# recorrelation_sweep passes it. None resolved to "routine",
|
||||
# which turned a reprocessed not-thin call into a thin one
|
||||
# that attaches with no fit check.
|
||||
severity=(_call_doc or {}).get("severity"),
|
||||
)
|
||||
if incident_id:
|
||||
incident_ids.append(incident_id)
|
||||
@@ -566,7 +574,7 @@ async def _run_intelligence_pipeline(
|
||||
if _flag("stt_enabled"):
|
||||
transcript, segments = await transcription.transcribe_call(
|
||||
call_id, gcs_uri, talkgroup_name,
|
||||
system_id=system_id, talkgroup_id=talkgroup_id,
|
||||
system_id=system_id, talkgroup_id=talkgroup_id, node_id=node_id,
|
||||
)
|
||||
else:
|
||||
scope = "globally" if not flags["stt_enabled"] else f"system {system_id}"
|
||||
|
||||
Reference in New Issue
Block a user