fix(trigger): near() fired bei Auto-Vorbeifahrten verpasst — Loop schneller + event-getrieben
Stefan ist mehrmals an einem 300m-near()-Watcher (DRK Kreyenbrueck)
vorbeigefahren, kein Fire. Ursache: Background-Loop tickte alle 30s,
Auto-Durchfahrt durch 600m-Durchmesser-Radius dauert bei 50-120 km/h
nur 18-43 Sekunden — der Tick konnte komplett dazwischen liegen.
Drei Fixes (A + B aus Stefans Vorschlag):
A1. Background-Loop-Frequenz: TICK_SEC 30 → 8.
Garantiert mind. 2 Checks auch bei 120 km/h durch 300m. Loop ist
billig (paar Dateilesungen + AST-Eval), Brain merkt das nicht.
A2. near() bekommt Age-Schutz (watcher.py NEAR_MAX_AGE_SEC=300):
Wenn location_age_sec > 5 min, gilt die Position als unbekannt
und near() liefert False. Verhindert Phantom-Fires wenn Tracking
aus ist oder Mobilfunk weg war — vorher haette der letzte
bekannte Wert weiter ausgewertet werden koennen.
B. Event-getriebener Tick:
- background.py: tick_now()-Funktion + Module-Slot fuer
agent_factory damit man von ausserhalb des Lifespan-Pfads
einen Tick triggern kann
- main.py: POST /triggers/check-now Endpoint ruft tick_now()
- bridge: _persist_location feuert nach jedem Save ein fire-and-
forget POST /triggers/check-now (run_in_executor, timeout 8s,
blockt nichts wenn Brain stockt)
Damit fires near() sofort wenn die App ein location_update schickt —
Polling ist nur noch der Fallback fuer Watcher OHNE GPS-Bezug
(disk_free, hour_of_day etc.) und als Sicherheits-Tick falls
location_update mal ausfaellt.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+28
-1
@@ -938,7 +938,12 @@ class ARIABridge:
|
||||
def _persist_location(self, location: Optional[dict]) -> None:
|
||||
"""Speichert die letzte bekannte GPS-Position fuer Watcher.
|
||||
Erwartet {lat, lon} oder {lat, lng}. Nicht-Dicts und fehlende
|
||||
Koordinaten werden ignoriert."""
|
||||
Koordinaten werden ignoriert.
|
||||
|
||||
Plus: triggert sofort einen on-demand Trigger-Check im Brain
|
||||
(POST /triggers/check-now). Ohne das wartet der Watcher-Loop
|
||||
bis zu TICK_SEC Sekunden — bei Auto-Vorbeifahrt durch einen
|
||||
300m-Radius (18-43s drin) kann das den Trigger verpassen."""
|
||||
if not isinstance(location, dict):
|
||||
return
|
||||
try:
|
||||
@@ -950,9 +955,31 @@ class ARIABridge:
|
||||
"lat": float(lat),
|
||||
"lon": float(lon),
|
||||
})
|
||||
except Exception:
|
||||
return
|
||||
# Fire-and-forget: Brain-on-demand-Tick. Wenn Brain nicht antwortet
|
||||
# oder langsam ist, blockt das nicht den GPS-Pfad.
|
||||
try:
|
||||
asyncio.create_task(self._trigger_brain_check_now())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
async def _trigger_brain_check_now(self) -> None:
|
||||
"""Brain-Endpoint POST /triggers/check-now anstossen."""
|
||||
brain_url = os.environ.get("BRAIN_URL", "http://aria-brain:8080")
|
||||
def _post():
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
f"{brain_url}/triggers/check-now",
|
||||
data=b"", method="POST",
|
||||
headers={"Content-Type": "application/json"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=8) as r:
|
||||
return r.status
|
||||
except Exception:
|
||||
return None
|
||||
await asyncio.get_event_loop().run_in_executor(None, _post)
|
||||
|
||||
def _persist_user_activity(self) -> None:
|
||||
"""Markiert dass der User gerade etwas gemacht hat (Chat/Voice).
|
||||
Watcher: last_user_message_ago_sec basiert darauf."""
|
||||
|
||||
Reference in New Issue
Block a user