correlator: judge each scene on its own embedding + severity (server-26#80, #95)
intelligence.py writes only the primary scene's embedding and severity to
calls/{id}. _build_context read them back off the call doc, so every
non-primary scene of a multi-scene call was correlated against scene 1's
semantic vector and severity rung: a scene about a different event scored
on the embedding path against the wrong incident, and could inherit a
minor/moderate/major severity it never had, clearing the creation gate on
borrowed weight. Same defect and same fix as the #87 coords leak.
- _build_context / preview_correlation / correlate_call: take embedding and
severity as params; drop the call_doc.get() fallbacks. A scene that
passes none has none, and is judged thin on its own signal.
- upload.py: both scene loops pass scene["embedding"] / scene["severity"];
_correlate_with_consensus forwards them. The no-scene unclassified branch
passes neither (correct: no scene, judged thin).
- recorrelation_sweep: passes the call doc's stored values explicitly
(whole-call re-link, link-only, so a borrowed severity cannot create).
- intelligence.py: SCENE DETECTION prompt tightened toward one scene
(server-26#5, partial) - MULTIPLE only for genuinely separate events,
"when unsure, one scene", plus a not-a-new-scene list.
- test_incident_identity.py: +2 regression tests mirroring the #87 test.
Full c2-core suite green (295 passed). #5 prompt change is unmeasured -
needs a scoped correlation-only window. Known remaining legs, tracked
separately: llm_correlator._call_block still reads the whole-call
transcript per scene; content-divergence veto skips on a None embedding.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
85393bdb26
commit
a9d1d2475a
@@ -254,6 +254,56 @@ async def test_a_scene_with_no_location_does_not_inherit_the_call_docs_pin():
|
||||
assert ctx["is_thin_call"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_scene_does_not_inherit_the_call_docs_embedding_or_severity():
|
||||
"""
|
||||
server-26#80 / #95. Same shape as the #87 coords leak above:
|
||||
intelligence.py writes only the PRIMARY scene's embedding and severity to
|
||||
calls/{id}. A non-primary scene being correlated must be judged on its own
|
||||
embedding (or none) and its own severity — not the call doc's — or a scene
|
||||
about a different event scores against the wrong incident on the embedding
|
||||
path and can inherit a minor/moderate/major rung it never had, clearing the
|
||||
creation gate on borrowed weight.
|
||||
"""
|
||||
with patch("app.internal.incident_correlator.fstore") as mock_fstore:
|
||||
mock_fstore.doc_get = AsyncMock(
|
||||
return_value={"embedding": [0.1] * 1536, "severity": "major"}
|
||||
)
|
||||
mock_fstore.collection_list = AsyncMock(return_value=[])
|
||||
ctx = await _build_context(
|
||||
call_id="call-scene-2", units=None, vehicles=None, cleared_units=None,
|
||||
location_coords=None, reference_time=NOW,
|
||||
system_id="sys-1", talkgroup_id=383, talkgroup_name=DISPATCH_TG,
|
||||
tags=[], incident_type="police", location=None,
|
||||
reassignment=False, create_if_new=True,
|
||||
embedding=None, severity=None,
|
||||
)
|
||||
assert ctx["call_embedding"] is None
|
||||
assert ctx["call_severity"] == "routine"
|
||||
assert ctx["is_thin_call"] is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_scene_is_judged_on_its_own_embedding_and_severity():
|
||||
"""The other half of #80/#95: the scene's own values are what land in ctx."""
|
||||
scene_vec = [0.9] * 1536
|
||||
with patch("app.internal.incident_correlator.fstore") as mock_fstore:
|
||||
mock_fstore.doc_get = AsyncMock(
|
||||
return_value={"embedding": [0.1] * 1536, "severity": "routine"}
|
||||
)
|
||||
mock_fstore.collection_list = AsyncMock(return_value=[])
|
||||
ctx = await _build_context(
|
||||
call_id="call-scene-2", units=None, vehicles=None, cleared_units=None,
|
||||
location_coords=None, reference_time=NOW,
|
||||
system_id="sys-1", talkgroup_id=383, talkgroup_name=DISPATCH_TG,
|
||||
tags=[], incident_type="police", location=None,
|
||||
reassignment=False, create_if_new=True,
|
||||
embedding=scene_vec, severity="major",
|
||||
)
|
||||
assert ctx["call_embedding"] == scene_vec
|
||||
assert ctx["call_severity"] == "major"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_a_bare_number_never_becomes_an_incident_location_or_title():
|
||||
inc = await _create(tags=["flames"], location="49", coords=None,
|
||||
|
||||
Reference in New Issue
Block a user