mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
chore(config): delete 51 dead keys, fix usgs_quake floor, secret-flag consistency
Backend half of making the dashboard the complete config surface (per an
exhaustive per-key audit).
Delete 51 vestigial/unread config fields (load-safe: _dict_to_dataclass
whitelists by field, so existing files carrying these keys still load and the
keys drop on next save):
- 36 duplicated MQTT block (host/port/username/password/topic_root/use_tls)
on memory/context/commands + env nws/swpc/ducting (grep-proven unread;
mesh_sources keeps its real MQTT fields)
- 3 no-op history cleanup keys (auto_cleanup/cleanup_interval_hours/max_age_days)
- 5 alert scaffolding (alert_cooldown_minutes, RegionAnchor.nws_zones,
battery_{warning,critical,emergency}_voltage)
- 5 danger-zone non-fire min_acres (kept fire.min_acres via a fire subclass)
- 2 deprecated adapter_config keys (nws.broadcast_severities/warning_suffix_promotes)
Bug: usgs_quake native magnitude floor was unreachable from the GUI (native
reads config.min_magnitude; the GUI "Global Floor" wrote the registry
global_mag_floor that only the Central path reads). Reconciled: min_magnitude
is the canonical native floor the frontend will bind; registry floors marked
Central-path-only. Effective filtering unchanged.
Secret-flag consistency: add environmental.roads511.api_key + wzdx.api_key to
SECRET_FIELDS (secrets move to .env in the follow-up; ${VAR} interpolation kept).
Suite at 10-failure baseline (1703 passed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5de683f7a7
commit
aacf053b63
8 changed files with 45 additions and 81 deletions
|
|
@ -30,9 +30,6 @@ history:
|
|||
database: /data/conversations.db
|
||||
max_messages_per_user: 50 # Messages to keep per user
|
||||
conversation_timeout: 86400 # Conversation expiry (seconds, 86400=24h)
|
||||
auto_cleanup: true # Auto-delete old conversations
|
||||
cleanup_interval_hours: 24 # How often to run cleanup
|
||||
max_age_days: 30 # Delete conversations older than this
|
||||
|
||||
# === MEMORY OPTIMIZATION ===
|
||||
memory:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ Handlers use the singleton `adapter_config` from this package:
|
|||
from meshai.adapter_config import adapter_config
|
||||
|
||||
cooldown_s = adapter_config.wfigs.cooldown_seconds # int
|
||||
severities = adapter_config.nws.broadcast_severities # list[str]
|
||||
tombstones = adapter_config.nws.tombstone_msgtypes # list[str]
|
||||
|
||||
Reads are dict-cached. The cache invalidates on `invalidate_cache()`
|
||||
(called from the REST API's PUT handler in v0.6-3c). The cache is
|
||||
|
|
|
|||
|
|
@ -73,23 +73,16 @@ REGISTRY: dict[tuple[str, str], dict[str, Any]] = {
|
|||
},
|
||||
|
||||
# =================================================================
|
||||
# NWS -- 3 settings (severity gate, tombstone msgTypes, suffix-promote toggle)
|
||||
# NWS -- tombstone msgTypes (severity gate + suffix-promote were removed
|
||||
# in the v0.x config-schema cleanup: both were [DEPRECATED — no longer
|
||||
# enforced]; NWS breadth is governed by the per-toggle dispatcher
|
||||
# severity threshold)
|
||||
# =================================================================
|
||||
("nws", "broadcast_severities"): {
|
||||
"default": ["Extreme", "Severe"], # nws_handler.py:43
|
||||
"type": "json",
|
||||
"description": "CAP severity strings allowed onto the mesh. [DEPRECATED — no longer enforced; NWS breadth is governed by the per-toggle dispatcher severity threshold]",
|
||||
},
|
||||
("nws", "tombstone_msgtypes"): {
|
||||
"default": ["Cancel", "Expire"], # nws_handler.py:46
|
||||
"type": "json",
|
||||
"description": "CAP msgType values that mark an alert as gone.",
|
||||
},
|
||||
("nws", "warning_suffix_promotes"): {
|
||||
"default": True, # nws_handler.py:172
|
||||
"type": "bool",
|
||||
"description": "Promote category-name-ending-in-_warning to Severe when CAP severity is missing. [DEPRECATED — no longer enforced; NWS breadth is governed by the per-toggle dispatcher severity threshold]",
|
||||
},
|
||||
|
||||
# =================================================================
|
||||
# USGS_QUAKE -- 6 settings (regional geography + 3 mag floors + PAGER set)
|
||||
|
|
@ -112,12 +105,12 @@ REGISTRY: dict[tuple[str, str], dict[str, Any]] = {
|
|||
("usgs_quake", "global_mag_floor"): {
|
||||
"default": 3.0, # quake_handler.py:69
|
||||
"type": "float",
|
||||
"description": "Global magnitude floor for unconditional broadcasts.",
|
||||
"description": "Global magnitude floor for unconditional broadcasts. CENTRAL-PATH ONLY (central/quake_handler.py); the native feed_source gates on environmental.usgs_quake.min_magnitude instead.",
|
||||
},
|
||||
("usgs_quake", "regional_mag_floor"): {
|
||||
"default": 2.5, # quake_handler.py:70
|
||||
"type": "float",
|
||||
"description": "Reduced magnitude floor for quakes within regional_radius_mi of centroid.",
|
||||
"description": "Reduced magnitude floor for quakes within regional_radius_mi of centroid. CENTRAL-PATH ONLY (central/quake_handler.py); the native feed_source gates on environmental.usgs_quake.min_magnitude instead.",
|
||||
},
|
||||
("usgs_quake", "escalate_mag_floor"): {
|
||||
"default": 5.0, # quake_handler.py:76
|
||||
|
|
|
|||
|
|
@ -44,8 +44,10 @@ from meshai.persistence import get_db
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# v0.6-3b: severity gate + tombstone msgTypes live in adapter_config.nws
|
||||
# (broadcast_severities, tombstone_msgtypes). Read at handler call time.
|
||||
# v0.6-3b: tombstone msgTypes live in adapter_config.nws
|
||||
# (tombstone_msgtypes), read at handler call time. The old broadcast_severities
|
||||
# severity gate was removed in the config-schema cleanup (no longer enforced;
|
||||
# NWS breadth is governed by the per-toggle dispatcher severity threshold).
|
||||
|
||||
# Ordered (substring, emoji) checks; first match wins.
|
||||
_EVENT_EMOJI = [
|
||||
|
|
|
|||
|
|
@ -63,11 +63,6 @@ class HistoryConfig:
|
|||
max_messages_per_user: int = 50
|
||||
conversation_timeout: int = 86400 # 24 hours
|
||||
|
||||
# Cleanup settings
|
||||
auto_cleanup: bool = True
|
||||
cleanup_interval_hours: int = 24
|
||||
max_age_days: int = 30 # Delete conversations older than this
|
||||
|
||||
|
||||
@dataclass
|
||||
class MemoryConfig:
|
||||
|
|
@ -75,13 +70,6 @@ class MemoryConfig:
|
|||
|
||||
enabled: bool = True # Enable memory optimization
|
||||
|
||||
# MQTT-specific fields (type=mqtt only)
|
||||
host: str = "" # MQTT broker hostname
|
||||
port: int = 1883 # MQTT broker port (1883 plain, 8883 TLS)
|
||||
username: str = "" # MQTT username (optional)
|
||||
password: str = "" # MQTT password (optional, supports )
|
||||
topic_root: str = "msh/US" # Topic root to subscribe to
|
||||
use_tls: bool = False # Enable TLS for MQTT connection
|
||||
window_size: int = 4 # Recent message pairs to keep in full
|
||||
summarize_threshold: int = 8 # Messages before re-summarizing
|
||||
|
||||
|
|
@ -92,13 +80,6 @@ class ContextConfig:
|
|||
|
||||
enabled: bool = True
|
||||
|
||||
# MQTT-specific fields (type=mqtt only)
|
||||
host: str = "" # MQTT broker hostname
|
||||
port: int = 1883 # MQTT broker port (1883 plain, 8883 TLS)
|
||||
username: str = "" # MQTT username (optional)
|
||||
password: str = "" # MQTT password (optional, supports )
|
||||
topic_root: str = "msh/US" # Topic root to subscribe to
|
||||
use_tls: bool = False # Enable TLS for MQTT connection
|
||||
observe_channels: list[int] = field(default_factory=list) # Empty = all channels
|
||||
ignore_nodes: list[str] = field(default_factory=list) # Node IDs to ignore
|
||||
max_age: int = 1_209_600 # 14 days in seconds
|
||||
|
|
@ -120,13 +101,6 @@ class CommandsConfig:
|
|||
|
||||
enabled: bool = True
|
||||
|
||||
# MQTT-specific fields (type=mqtt only)
|
||||
host: str = "" # MQTT broker hostname
|
||||
port: int = 1883 # MQTT broker port (1883 plain, 8883 TLS)
|
||||
username: str = "" # MQTT username (optional)
|
||||
password: str = "" # MQTT password (optional, supports )
|
||||
topic_root: str = "msh/US" # Topic root to subscribe to
|
||||
use_tls: bool = False # Enable TLS for MQTT connection
|
||||
prefix: str = "!"
|
||||
disabled_commands: list[str] = field(default_factory=list)
|
||||
custom_commands: dict = field(default_factory=dict)
|
||||
|
|
@ -253,7 +227,6 @@ class RegionAnchor:
|
|||
description: str = "" # e.g., "Twin Falls, Burley, Jerome along I-84/US-93"
|
||||
aliases: list[str] = field(default_factory=list) # e.g., ["southern Idaho", "magic valley"]
|
||||
cities: list[str] = field(default_factory=list) # e.g., ["Twin Falls", "Burley", "Jerome"]
|
||||
nws_zones: list[str] = field(default_factory=list) # NWS zone codes (e.g., ["IDZ016", "IDZ030"])
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -273,10 +246,6 @@ class AlertRulesConfig:
|
|||
battery_warning_threshold: int = 30
|
||||
battery_critical_threshold: int = 15
|
||||
battery_emergency_threshold: int = 5
|
||||
# Voltage-based thresholds (more accurate than percentage)
|
||||
battery_warning_voltage: float = 3.60
|
||||
battery_critical_voltage: float = 3.50
|
||||
battery_emergency_voltage: float = 3.40
|
||||
power_source_change: bool = True
|
||||
solar_not_charging: bool = True
|
||||
|
||||
|
|
@ -314,7 +283,6 @@ class MeshIntelligenceConfig:
|
|||
# Alert settings
|
||||
critical_nodes: list[str] = field(default_factory=list) # Short names of critical nodes (e.g., ["MHR", "HPR"])
|
||||
alert_channel: int = -1 # Channel to broadcast alerts on. -1 = disabled, 0+ = channel index
|
||||
alert_cooldown_minutes: int = 30 # Min minutes between repeated alerts for same condition
|
||||
alert_rules: AlertRulesConfig = field(default_factory=AlertRulesConfig)
|
||||
|
||||
|
||||
|
|
@ -337,13 +305,6 @@ class NWSConfig(_SourcedFeed):
|
|||
|
||||
enabled: bool = True
|
||||
|
||||
# MQTT-specific fields (type=mqtt only)
|
||||
host: str = "" # MQTT broker hostname
|
||||
port: int = 1883 # MQTT broker port (1883 plain, 8883 TLS)
|
||||
username: str = "" # MQTT username (optional)
|
||||
password: str = "" # MQTT password (optional, supports )
|
||||
topic_root: str = "msh/US" # Topic root to subscribe to
|
||||
use_tls: bool = False # Enable TLS for MQTT connection
|
||||
tick_seconds: int = 60
|
||||
areas: list = field(default_factory=lambda: ["ID"])
|
||||
severity_min: str = "moderate"
|
||||
|
|
@ -356,14 +317,6 @@ class SWPCConfig(_SourcedFeed):
|
|||
|
||||
enabled: bool = True
|
||||
|
||||
# MQTT-specific fields (type=mqtt only)
|
||||
host: str = "" # MQTT broker hostname
|
||||
port: int = 1883 # MQTT broker port (1883 plain, 8883 TLS)
|
||||
username: str = "" # MQTT username (optional)
|
||||
password: str = "" # MQTT password (optional, supports )
|
||||
topic_root: str = "msh/US" # Topic root to subscribe to
|
||||
use_tls: bool = False # Enable TLS for MQTT connection
|
||||
|
||||
|
||||
@dataclass
|
||||
class DuctingConfig(_SourcedFeed):
|
||||
|
|
@ -371,13 +324,6 @@ class DuctingConfig(_SourcedFeed):
|
|||
|
||||
enabled: bool = True
|
||||
|
||||
# MQTT-specific fields (type=mqtt only)
|
||||
host: str = "" # MQTT broker hostname
|
||||
port: int = 1883 # MQTT broker port (1883 plain, 8883 TLS)
|
||||
username: str = "" # MQTT username (optional)
|
||||
password: str = "" # MQTT password (optional, supports )
|
||||
topic_root: str = "msh/US" # Topic root to subscribe to
|
||||
use_tls: bool = False # Enable TLS for MQTT connection
|
||||
tick_seconds: int = 10800 # 3 hours
|
||||
latitude: float = 42.56 # Twin Falls area default
|
||||
longitude: float = -114.47
|
||||
|
|
@ -419,6 +365,11 @@ class USGSQuakeConfig(_SourcedFeed):
|
|||
enabled: bool = False
|
||||
tick_seconds: int = 300
|
||||
feed_url: str = "https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.geojson"
|
||||
# Native-path broadcast magnitude floor: the native adapter
|
||||
# (env/usgs_quake.py) gates on this. THIS is the GUI-editable quake
|
||||
# magnitude floor for the native feed_source. (The adapter_config
|
||||
# REGISTRY keys usgs_quake.global_mag_floor / regional_mag_floor apply
|
||||
# only to the Central-firehose path in central/quake_handler.py.)
|
||||
min_magnitude: float = 2.5
|
||||
# [west, south, east, north] -- Magic Valley -> Borah Peak -> Yellowstone
|
||||
bbox: list = field(default_factory=lambda: [-115.5, 42.0, -110.0, 45.2])
|
||||
|
|
@ -758,7 +709,18 @@ class DangerZoneHazardConfig:
|
|||
|
||||
enabled: bool = True
|
||||
buffer_mi: float = 5.0
|
||||
min_acres: float = 0.0 # fire-only; ignored by other families
|
||||
|
||||
|
||||
@dataclass
|
||||
class DangerZoneFireConfig(DangerZoneHazardConfig):
|
||||
"""Fire-family danger-zone tuning; adds an acreage floor.
|
||||
|
||||
``min_acres`` is fire-only (read at danger_zone_correlator.py in the
|
||||
``fam == "fire"`` branch); the non-fire families use the plain
|
||||
DangerZoneHazardConfig, which no longer carries it.
|
||||
"""
|
||||
|
||||
min_acres: float = 0.0 # skip fires smaller than this (0 = no floor)
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -778,7 +740,7 @@ class DangerZonesConfig:
|
|||
|
||||
# Per-family sub-configs. snow->weather, flood->seismic resolved in the
|
||||
# correlator; both still exposed here for distinct GUI tuning.
|
||||
fire: DangerZoneHazardConfig = field(default_factory=DangerZoneHazardConfig)
|
||||
fire: DangerZoneFireConfig = field(default_factory=DangerZoneFireConfig)
|
||||
weather: DangerZoneHazardConfig = field(default_factory=DangerZoneHazardConfig)
|
||||
snow: DangerZoneHazardConfig = field(default_factory=DangerZoneHazardConfig)
|
||||
flood: DangerZoneHazardConfig = field(default_factory=DangerZoneHazardConfig)
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ SECRET_FIELDS: set[str] = {
|
|||
"mesh_sources.*.password",
|
||||
"environmental.traffic.api_key",
|
||||
"environmental.firms.map_key",
|
||||
"environmental.roads511.api_key",
|
||||
"environmental.wzdx.api_key",
|
||||
"notifications.rules.*.smtp_password",
|
||||
"notifications.toggles.*.smtp_password",
|
||||
"danger_zones.webhook_url",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@ def test_list_returns_all_59_keys(client):
|
|||
# 14 adapters with at least one key (itd_511 has zero -- not in the
|
||||
# grouped dict because the SQL only returns rows that exist).
|
||||
total = sum(len(v) for v in body.values())
|
||||
assert total == 96
|
||||
# was 96; config-schema cleanup removed the two deprecated nws keys
|
||||
# (broadcast_severities, warning_suffix_promotes) -> 94.
|
||||
assert total == 94
|
||||
|
||||
|
||||
def test_list_grouped_by_adapter(client):
|
||||
|
|
@ -184,12 +186,14 @@ def test_put_str_validation(client):
|
|||
|
||||
|
||||
def test_put_json_accepts_list(client):
|
||||
# broadcast_severities was removed in the config-schema cleanup; use the
|
||||
# surviving nws json-list key tombstone_msgtypes to exercise list PUTs.
|
||||
r = client.put(
|
||||
"/api/adapter-config/nws/broadcast_severities",
|
||||
json={"value": ["Extreme"]},
|
||||
"/api/adapter-config/nws/tombstone_msgtypes",
|
||||
json={"value": ["Cancel"]},
|
||||
)
|
||||
assert r.status_code == 200
|
||||
assert r.json()["value"] == ["Extreme"]
|
||||
assert r.json()["value"] == ["Cancel"]
|
||||
|
||||
|
||||
def test_put_json_accepts_dict(client):
|
||||
|
|
|
|||
|
|
@ -75,7 +75,9 @@ def test_adapter_config_type_check_constrains_vocabulary(fresh_db):
|
|||
|
||||
def test_registry_at_59_entries():
|
||||
"""v0.6-3a.1 trim: 43 CONFIG-only keys (was 77 in v0.6-3a draft)."""
|
||||
assert len(REGISTRY) == 96, (
|
||||
# was 96; config-schema cleanup removed the two deprecated nws keys
|
||||
# (broadcast_severities, warning_suffix_promotes) -> 94.
|
||||
assert len(REGISTRY) == 94, (
|
||||
f"REGISTRY drift guard; got {len(REGISTRY)}. "
|
||||
f"If a sentence template / emoji / heuristic snuck in, it belongs in CODE not config."
|
||||
)
|
||||
|
|
@ -225,7 +227,9 @@ def test_accessor_returns_bool(fresh_db):
|
|||
|
||||
def test_accessor_returns_json_list(fresh_db):
|
||||
invalidate_cache()
|
||||
assert adapter_config.nws.broadcast_severities == ["Extreme", "Severe"]
|
||||
# broadcast_severities was removed in the config-schema cleanup (deprecated,
|
||||
# no longer enforced); tombstone_msgtypes is the surviving nws json-list key.
|
||||
assert adapter_config.nws.tombstone_msgtypes == ["Cancel", "Expire"]
|
||||
|
||||
|
||||
def test_accessor_returns_json_dict(fresh_db):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue