Spaces:
Sleeping
Sleeping
Upload jarvis_core/events.py with huggingface_hub
Browse files- jarvis_core/events.py +92 -0
jarvis_core/events.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""jarvis_core.events — proactive event engine.
|
| 2 |
+
|
| 3 |
+
Event: {event_id, type, timestamp, source, payload, priority}
|
| 4 |
+
Decision: IGNORE LOG NOTIFY CREATE_TASK EXECUTE_ACTION ASK_USER
|
| 5 |
+
Proactive actions NEVER bypass approvals (see approvals.py).
|
| 6 |
+
"""
|
| 7 |
+
import time
|
| 8 |
+
import uuid
|
| 9 |
+
|
| 10 |
+
from .store import get_store, redact
|
| 11 |
+
|
| 12 |
+
COL = "jarvis_events"
|
| 13 |
+
DECISIONS = {"IGNORE", "LOG", "NOTIFY", "CREATE_TASK", "EXECUTE_ACTION", "ASK_USER"}
|
| 14 |
+
|
| 15 |
+
# type -> (decision, min_priority)
|
| 16 |
+
_RULES = {
|
| 17 |
+
"pc.online": ("NOTIFY", 0),
|
| 18 |
+
"pc.offline": ("LOG", 0),
|
| 19 |
+
"telegram.message": ("LOG", 0),
|
| 20 |
+
"n8n.completed": ("LOG", 0),
|
| 21 |
+
"n8n.failed": ("NOTIFY", 0),
|
| 22 |
+
"mission.blocked": ("NOTIFY", 0),
|
| 23 |
+
"task.due": ("EXECUTE_ACTION", 0),
|
| 24 |
+
"job.finished": ("LOG", 0),
|
| 25 |
+
"job.failed": ("NOTIFY", 0),
|
| 26 |
+
"mcp.failed": ("CREATE_TASK", 0),
|
| 27 |
+
"brain failing": ("NOTIFY", 0),
|
| 28 |
+
"service.down": ("NOTIFY", 0),
|
| 29 |
+
"routine.due": ("EXECUTE_ACTION", 0),
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
_notifiers = [] # fn(text) — e.g. Telegram sender
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def on_notify(fn):
|
| 36 |
+
_notifiers.append(fn)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def emit(etype, source="system", payload=None, priority=0):
|
| 40 |
+
store = get_store()
|
| 41 |
+
eid = store.add(COL, {
|
| 42 |
+
"event_id": f"ev-{uuid.uuid4().hex[:8]}",
|
| 43 |
+
"type": etype, "timestamp": time.time(), "source": source,
|
| 44 |
+
"payload": {k: redact(v)[:300] for k, v in (payload or {}).items()},
|
| 45 |
+
"priority": priority,
|
| 46 |
+
})
|
| 47 |
+
ev = store.get(COL, eid)
|
| 48 |
+
decision = classify(ev)
|
| 49 |
+
ev["decision"] = decision
|
| 50 |
+
store.set(COL, eid, ev)
|
| 51 |
+
dispatch(ev, decision)
|
| 52 |
+
return ev
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def classify(ev):
|
| 56 |
+
rule = _RULES.get(ev.get("type"))
|
| 57 |
+
if not rule:
|
| 58 |
+
return "LOG"
|
| 59 |
+
decision, min_pri = rule
|
| 60 |
+
if ev.get("priority", 0) < min_pri:
|
| 61 |
+
return "LOG"
|
| 62 |
+
return decision
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def dispatch(ev, decision):
|
| 66 |
+
text = f"{ev.get('type')} from {ev.get('source')}: " \
|
| 67 |
+
f"{redact(str(ev.get('payload')) )[:200]}"
|
| 68 |
+
if decision == "NOTIFY":
|
| 69 |
+
for fn in _notifiers:
|
| 70 |
+
try:
|
| 71 |
+
fn(f"JARVIS event, sir — {text}")
|
| 72 |
+
except Exception:
|
| 73 |
+
pass
|
| 74 |
+
elif decision == "CREATE_TASK":
|
| 75 |
+
try:
|
| 76 |
+
from . import jobs
|
| 77 |
+
jobs.enqueue("note", {"text": f"auto-task from event: {text}"},
|
| 78 |
+
idempotency_key=ev.get("event_id", ""))
|
| 79 |
+
except Exception:
|
| 80 |
+
pass
|
| 81 |
+
if decision in ("LOG", "NOTIFY", "CREATE_TASK", "EXECUTE_ACTION", "ASK_USER"):
|
| 82 |
+
try:
|
| 83 |
+
from . import audit
|
| 84 |
+
audit.record("system", ev.get("source", "?"), f"event:{ev.get('type')}",
|
| 85 |
+
tool="events", status="SUCCESS", result=text[:200])
|
| 86 |
+
except Exception:
|
| 87 |
+
pass
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
def recent(limit=10):
|
| 91 |
+
items = get_store().all(COL)
|
| 92 |
+
return sorted(items, key=lambda e: e.get("timestamp", 0), reverse=True)[:limit]
|