mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
fire: remove fire digest feature and drop out-of-coverage fires at ingest (#107)
Two fire-scope cleanups: 1. Remove the fire digest feature entirely -- scheduler (notifications/scheduled/fire_digest.py), pipeline wiring, the fires.digest_* adapter_config key registrations, and the Fire Digest dashboard UI (ScheduledBroadcasts / Environment / Reference / AdapterConfig / ActivityLog). The unrelated generic per-rule notification digest is kept. Orphaned fires.digest_* config rows and the fire_digest_broadcasts table are left as inert data (v16 migration untouched). 2. Add a coverage-scope gate at fire ingest: _ingest_fires now skips any fire whose coordinates fall outside all configured coverage areas (same areas_from_config + classify_geom_areas membership the dispatch-level CoverageFilter uses), so out-of-coverage fires are never stored, tracked, alerted, reminded, or re-ingested. Fails open when coverage is disabled, has no areas, or excludes the fires adapter. Co-authored-by: Matt Johnson <mj@k7zvx.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
af826319c8
commit
b0b0697bac
16 changed files with 189 additions and 960 deletions
|
|
@ -59,79 +59,6 @@ def test_router_scope_type_defined_before_env_check():
|
|||
or "scope_type:" in preceding
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# adapter_config seed + categories registration
|
||||
# ===========================================================================
|
||||
|
||||
|
||||
def test_adapter_config_seeds_digest_keys():
|
||||
from meshai.persistence import get_db
|
||||
rows = {
|
||||
(r["adapter"], r["key"]): r["default_json"]
|
||||
for r in get_db().execute(
|
||||
"SELECT adapter, key, default_json FROM adapter_config "
|
||||
"WHERE adapter='fires' AND key LIKE 'digest%'"
|
||||
)
|
||||
}
|
||||
assert rows[("fires", "digest_enabled")] == "true"
|
||||
assert rows[("fires", "digest_schedule")] == '["06:00", "18:00"]'
|
||||
assert rows[("fires", "digest_timezone")] == '"America/Boise"'
|
||||
assert rows[("fires", "digest_max_chars")] == "140"
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# Digest renderer
|
||||
# ===========================================================================
|
||||
|
||||
|
||||
def test_render_digest_returns_no_fires_when_table_empty():
|
||||
from meshai.notifications.scheduled.fire_digest import render_digest
|
||||
|
||||
async def _run():
|
||||
return await render_digest(now=None)
|
||||
wire, source = asyncio.run(_run())
|
||||
assert wire == ""
|
||||
assert source == "no_fires"
|
||||
|
||||
|
||||
def test_render_digest_terse_fallback_when_no_llm():
|
||||
_seed_fire(irwin_id="ID-A", name="Cache Peak",
|
||||
lat=42.0, lon=-114.0, acres=1847, contained=23)
|
||||
_seed_fire(irwin_id="ID-B", name="Twin Peaks",
|
||||
lat=43.0, lon=-115.0, acres=320, contained=5)
|
||||
from meshai.notifications.scheduled.fire_digest import render_digest
|
||||
|
||||
async def _run():
|
||||
return await render_digest(now=None)
|
||||
wire, source = asyncio.run(_run())
|
||||
assert source == "deterministic"
|
||||
assert wire
|
||||
assert "Cache Peak" in wire
|
||||
assert len(wire) <= 140
|
||||
|
||||
|
||||
def test_render_digest_uses_llm_when_available():
|
||||
"""When the LLM backend returns a string, that string IS the wire."""
|
||||
_seed_fire(irwin_id="ID-A", name="Cache Peak",
|
||||
lat=42.0, lon=-114.0, acres=1847)
|
||||
|
||||
class StubLLM:
|
||||
async def generate(self, *, messages, system_prompt, max_tokens):
|
||||
# The renderer must give us a single-line wire derived from
|
||||
# the LLM output, with markdown stripped + cap applied.
|
||||
return "Cache Peak 1847 ac stable; no spotting today."
|
||||
|
||||
from meshai.notifications.scheduled.fire_digest import render_digest
|
||||
|
||||
async def _run():
|
||||
return await render_digest(now=None)
|
||||
wire, source = asyncio.run(_run())
|
||||
assert source == "deterministic"
|
||||
# render_digest is now fully deterministic (no LLM backend).
|
||||
assert "Cache Peak" in wire
|
||||
assert "1,847 ac" in wire
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# Natural-language fire DMs route to the LLM (no ?status fallback)
|
||||
# ===========================================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue